Compare commits

...

38 Commits
main ... v3.1.1

Author SHA1 Message Date
rakita 15b5581894
Set alpine version to 3.12.3 (#231)
Alpine version is changed in order to prevent Cmake errors. Reference: https://gitlab.alpinelinux.org/alpine/aports/-/issues/12321.

Intends to solve https://github.com/openethereum/openethereum/issues/230

Co-authored-by: Denis Granha <denis@gnosis.pm>
2021-01-19 12:36:10 +01:00
draganrakita 05ac033907 Bump to 3.1.1 2021-01-18 22:43:19 +01:00
rakita cbf0b00c1d Added additional Sg-1,Ca-2,Ca-3 OE bootnodes (#222) 2021-01-18 22:43:19 +01:00
draganrakita 71bc62609e cicd fix v2 2020-12-21 16:13:17 +01:00
draganrakita 7336511246 cicd fix 2020-12-21 12:28:58 +01:00
draganrakita eb9041691e Disable windows2019, remove tagged windows artifact 2020-12-21 10:49:00 +01:00
rakita 51d8ac4219
Revert "Fix CI problems (#127)" and Remove sscache (#181)
This reverts commit 12afb13e9b.
2020-12-18 09:28:05 +01:00
draganrakita 64eb48ef39 Bump to 3.1.1-rc.1 2020-12-15 17:22:03 +01:00
draganrakita 0138e5c71f Tweaks in informer log 2020-12-15 17:21:41 +01:00
adria0.eth cf76d41bf4 Update ISSUE_TEMPLATE.md (#124)
Tell users to ask questions in discord instead of opening an issue.
2020-12-15 11:43:22 +01:00
Giacomo 2161bff690 Add custom windows runner (#162) 2020-12-15 11:43:22 +01:00
rakita 07a7281e26 Ancient target set. InvalidStateRoot bug (#69) (#149) 2020-12-15 11:43:22 +01:00
rakita fb975731bb TypedTransaction (EIP-2718) and Optional access list (EIP-2930) (#135) 2020-12-15 11:43:22 +01:00
rakita 1bce9fa76d Fix broken doc comments (#151) 2020-12-15 11:43:21 +01:00
rakita 8f794afdb5 Add ws-max-paxload (#155) 2020-12-15 11:43:21 +01:00
rakita f9f536cd08 Trace comment on new block inclusion (#100) 2020-12-15 11:43:21 +01:00
rakita bfb65140d2 Remove sscache (#138) 2020-12-15 11:43:21 +01:00
rakita 5e2cadd9c7 Add flag to disable storage output in openethereum-evm tool #97 (#115) 2020-12-15 11:43:21 +01:00
adria0.eth 12afb13e9b Fix CI problems (#127)
* Temporally Fix CI compilation
2020-12-15 11:43:21 +01:00
Adria Massanet 4435e6645c Update linked-hash-map to 0.5.3 2020-12-15 11:43:21 +01:00
Lachezar Lechev 90e5eeefb8 ethstore - remove unnecessary dir & tiny-keccak dependencies from the lib (#107)
* ethstore - remove unnecessary dir & tiny-keccak dependencies
2020-12-15 11:43:20 +01:00
Giacomo c33d7fbb45 Fix deprecated set-env declaration (#106)
* Fix deprecated set-env declaration

* Fix add-path and set-env in install-sscache.ps1
2020-12-15 11:43:20 +01:00
Giacomo 3bbb647e5f Feature/improve dockerhub deployment (#98)
* Add docker deployment for Github actions

* Add docker specs for the release of tagged and latest versions, also add specs for nightly builds

Co-authored-by: Denis Granha <denis@gnosis.pm>
2020-12-15 11:43:20 +01:00
adria0.eth 007207d8ca EIP2565 impl (#82)
EIP2565 implementation
2020-12-15 11:43:20 +01:00
adria0.eth c8aea223ec Downgrade sccache to 1.1.2 (#93) 2020-12-15 11:43:20 +01:00
adria0.eth d52272983a EIP2929 with journaling + Yolov3 (#79) 2020-12-15 11:43:20 +01:00
rakita 905d76b436 Sync block verification (#74)
* Synchronize block verification
* max_round_blocks_to_import set to 1
* Fixed test that rely on 12block batches

Co-authored-by: adria0.eth <5526331+adria0@users.noreply.github.com>
2020-12-15 11:43:19 +01:00
Adria Massanet e69d2009cf Update gitactions for dev branch, deprecate stable branch 2020-12-15 11:43:19 +01:00
varasev a95b2de645 Add `wasmDisableTransition` spec option (#60)
* Add wasmDisableTransition spec option
2020-12-15 11:43:19 +01:00
adria0.eth 6ecc6ddcdf Fix warnings (#64) 2020-12-15 11:43:19 +01:00
rakita 6ecb66eff9 Change wiki links (#68) 2020-12-15 11:43:19 +01:00
Denis Granha 94a784bfe9
Add docker deployment for Github actions 2020-11-06 12:02:27 +01:00
draganrakita 2072341eca Bump to 3.1.0 2020-11-02 13:28:44 +01:00
Denis Granha 36d250a420 revert actions/cache to version 1.1.2 (#80) 2020-11-02 13:20:20 +01:00
rakita 6f7548f596 Use ubuntu-16.04 for glibc compatibility (#11888) (#73) 2020-11-02 13:20:07 +01:00
adria0.eth 1ccbe5cfd4 Update gitactions master->main (#72) 2020-11-02 13:19:11 +01:00
Denis Granha a8242ffef9 fix CD env param 2020-11-02 13:18:50 +01:00
Giacomo fb071acb09 Set AWS_REGION as a global env variable (#67) 2020-11-02 13:18:42 +01:00
153 changed files with 3868 additions and 1484 deletions

View File

@ -2,7 +2,7 @@
## Do you have a question?
Check out our [Basic Usage](https://openethereum.github.io/wiki/Basic-Usage), [Configuration](https://openethereum.github.io/wiki/Configuring-OpenEthereum), and [FAQ](https://openethereum.github.io/wiki/FAQ) articles on our [wiki](https://openethereum.github.io/wiki)!
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.
@ -35,7 +35,7 @@ There are a few basic ground-rules for contributors (including the maintainer(s)
* **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.
* Contributors should adhere to the [Parity Ethereum Style Guide](https://openethereum.github.io/wiki/Parity-Ethereum-Style-Guide).
* All code changed should be formated by running `cargo fmt -- --config=merge_imports=true`
### Recommendations

View File

@ -1,6 +1,8 @@
For questions please use https://discord.io/openethereum, issues are for bugs and feature requests.
_Before filing a new issue, please **provide the following information**._
- **OpenEthereum version**: 0.0.0
- **OpenEthereum version (>=3.1.0)**: 0.0.0
- **Operating system**: Windows / MacOS / Linux
- **Installation**: homebrew / one-line installer / built from source
- **Fully synchronized**: no / yes

View File

@ -4,26 +4,22 @@ on:
pull_request:
push:
branches:
- master
- stable
- main
- dev
jobs:
build-tests:
name: Test and Build
env:
SCCACHE_CACHE_SIZE: "1G"
SCCACHE_IDLE_TIMEOUT: 0
strategy:
matrix:
platform:
- ubuntu-20.04
- ubuntu-16.04
- macos-latest
- windows-latest
toolchain:
- stable
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources
uses: actions/checkout@master
uses: actions/checkout@main
with:
submodules: true
- name: Install toolchain
@ -32,68 +28,8 @@ jobs:
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

View File

@ -2,107 +2,66 @@ name: Build Release Suite
on:
push:
branches:
- stable
tags:
- v*
# Global vars
env:
AWS_REGION: "us-east-1"
AWS_S3_ARTIFACTS_BUCKET: "openethereum-releases"
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
jobs:
build:
name: Build Release
env:
SCCACHE_CACHE_SIZE: "1G"
SCCACHE_IDLE_TIMEOUT: 0
AWS_S3_ARTIFACTS_BUCKET: "openethereum-releases"
AWS_REGION: "us-east-1"
strategy:
matrix:
platform:
- ubuntu-20.04
- ubuntu-16.04
- macos-latest
- windows-latest
toolchain:
- stable
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources
uses: actions/checkout@master
uses: actions/checkout@main
- 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: Install LLVM for Windows
# if: matrix.platform == 'windows2019'
# run: choco install llvm
- name: Sccache statistics
run: sccache --show-stats
# - name: Build OpenEthereum for Windows
# if: matrix.platform == 'windows2019'
# run: sh scripts/actions/build-windows.sh ${{matrix.platform}}
- 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
# - name: Upload Windows build
# uses: actions/upload-artifact@v2
# if: matrix.platform == 'windows2019'
# with:
# name: windows-artifacts
# path: artifacts
# ==============================
# Linux/Macos Build
# ==============================
- name: Build OpenEthereum for ${{matrix.platform}}
if: matrix.platform != 'windows-latest'
if: matrix.platform != 'windows2019'
run: sh scripts/actions/build-linux.sh ${{matrix.platform}}
- name: Upload Linux build
uses: actions/upload-artifact@v2
if: matrix.platform == 'ubuntu-20.04'
if: matrix.platform == 'ubuntu-16.04'
with:
name: linux-artifacts
path: artifacts
@ -114,35 +73,23 @@ jobs:
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
runs-on: ubuntu-16.04
steps:
- name: Set env
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
# ==============================
# Create ZIP files
# ==============================
- name: Download Windows artifacts
uses: actions/download-artifact@v2
with:
name: windows-artifacts
path: windows-artifacts
# - 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
@ -185,18 +132,18 @@ jobs:
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 .
# - 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}')
# 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
@ -215,11 +162,11 @@ jobs:
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
# - 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
@ -237,23 +184,23 @@ jobs:
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/
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 }}
# 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
runs-on: ubuntu-16.04
steps:
- name: Set env
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
# ==============================
# Download artifacts
@ -269,10 +216,10 @@ jobs:
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: Download artifacts
# uses: actions/download-artifact@v2
# with:
# name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
- name: Display structure of downloaded files
run: ls
@ -326,13 +273,13 @@ jobs:
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
# - 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

View File

@ -4,18 +4,15 @@ on:
pull_request:
push:
branches:
- master
- stable
- main
- dev
jobs:
check:
name: Check
runs-on: ubuntu-20.04
env:
SCCACHE_CACHE_SIZE: "1G"
SCCACHE_IDLE_TIMEOUT: 0
runs-on: ubuntu-16.04
steps:
- name: Checkout sources
uses: actions/checkout@master
uses: actions/checkout@main
with:
submodules: true
- name: Install stable toolchain
@ -24,32 +21,6 @@ jobs:
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:
@ -77,10 +48,3 @@ jobs:
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

View File

@ -0,0 +1,29 @@
name: Docker Image Nightly Release
# Run "nightly" build on each commit to "dev" branch.
on:
push:
branches:
- dev
jobs:
deploy-docker:
name: Build Release
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@master
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Deploy to docker hub
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: openethereum/openethereum
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
dockerfile: scripts/docker/alpine/Dockerfile
tags: "nightly"

30
.github/workflows/deploy-docker-tag.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Docker Image Tag and Latest Release
on:
push:
tags:
- v*
jobs:
deploy-docker:
name: Build Release
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@master
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Deploy to docker hub
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: openethereum/openethereum
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
dockerfile: scripts/docker/alpine/Dockerfile
tags: "latest,${{ env.RELEASE_VERSION }}"

30
.github/workflows/deploy-docker.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Docker Image Release
on:
push:
branches:
- main
tags:
- v*
jobs:
deploy-docker:
name: Build Release
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@master
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Deploy to docker hub
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: openethereum/openethereum
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
dockerfile: scripts/docker/alpine/Dockerfile
tag_names: true

View File

@ -1,34 +1,56 @@
## OpenEthereum v3.1RC1
## OpenEthereum v3.1.1
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.
Bug fixes:
* Ancient target set. InvalidStateRoot bug (#69) (#149)
* Update linked-hash-map to 0.5.3
Enhancements:
* Added additional Sg-1,Ca-2,Ca-3 OE bootnodes
* Add --ws-max-paxload (#155)
* Add flag to disable storage output in openethereum-evm tool #97 (#115)
* ethstore - remove unnecessary dir & tiny-keccak dependencies from the lib (#107)
* Sync block verification (#74)
* Add `wasmDisableTransition` spec option (#60)
* EIP2929 with journaling + Yolov3 (#79)
* EIP2565 impl (#82)
* TypedTransaction (EIP-2718) and Optional access list (EIP-2930) (#135)
DevOps:
* Add custom windows runner (#162)
* Remove sscache (#138)
* Fix deprecated set-env declaration (#106)
## OpenEthereum v3.1.0
OpenEthereum 3.1.0 is a 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
The full list of included changes from v2.5.13 to v3.1.0:
* Use ubuntu-16.04 for glibc compatibility (#11888) (#73)
* 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

32
Cargo.lock generated
View File

@ -447,6 +447,9 @@ dependencies = [
"rlp 0.3.0",
"rlp_derive",
"rustc-hex 1.0.0",
"serde",
"serde_json",
"serde_repr",
"unexpected",
]
@ -1100,6 +1103,7 @@ dependencies = [
"num",
"parity-bytes",
"parity-crypto 0.4.2",
"rustc-hex 1.0.0",
]
[[package]]
@ -1460,7 +1464,6 @@ dependencies = [
name = "ethstore"
version = "0.2.1"
dependencies = [
"dir",
"ethereum-types 0.4.2",
"ethkey",
"itertools 0.5.10",
@ -1479,7 +1482,6 @@ dependencies = [
"smallvec 0.6.13",
"tempdir",
"time",
"tiny-keccak 1.5.0",
]
[[package]]
@ -1505,6 +1507,7 @@ version = "0.1.0"
dependencies = [
"bit-set",
"criterion 0.2.11",
"ethcore-builtin",
"ethereum-types 0.4.2",
"heapsize",
"hex-literal",
@ -2438,9 +2441,9 @@ dependencies = [
[[package]]
name = "linked-hash-map"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]]
name = "local-encoding"
@ -2862,7 +2865,7 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "openethereum"
version = "3.1.0-rc1"
version = "3.1.1"
dependencies = [
"ansi_term 0.10.2",
"atty",
@ -3003,7 +3006,7 @@ dependencies = [
"rustc-hex 2.0.1",
"scrypt 0.2.0",
"sha2 0.8.0",
"subtle 2.1.0",
"subtle 2.3.0",
"tiny-keccak 1.5.0",
"zeroize",
]
@ -3227,7 +3230,7 @@ dependencies = [
[[package]]
name = "parity-version"
version = "3.1.0-rc1"
version = "3.1.1"
dependencies = [
"parity-bytes",
"rlp 0.3.0",
@ -4274,6 +4277,17 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
dependencies = [
"proc-macro2 1.0.20",
"quote 1.0.7",
"syn 1.0.40",
]
[[package]]
name = "sha-1"
version = "0.8.1"
@ -4456,9 +4470,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "subtle"
version = "2.1.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829"
checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
[[package]]
name = "syn"

View File

@ -2,7 +2,7 @@
description = "OpenEthereum"
name = "openethereum"
# NOTE Make sure to update util/version/Cargo.toml as well
version = "3.1.0-rc1"
version = "3.1.1"
license = "GPL-3.0"
authors = [
"OpenEthereum developers",

View File

@ -46,7 +46,7 @@ OpenEthereum's goal is to be the fastest, lightest, and most secure Ethereum cli
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.
If you run into problems while using OpenEthereum, check out the [old wiki for documentation](https://openethereum.github.io/wiki/), 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 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!
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.
@ -135,7 +135,7 @@ To start OpenEthereum as a regular user using `systemd` init:
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 old wiki](https://openethereum.github.io/wiki/Configuring-OpenEthereum) for details.
3. To configure OpenEthereum, see [our wiki](https://openethereum.github.io/Configuring-OpenEthereum) for details.
## 4. Testing <a id="chapter-004"></a>
@ -157,7 +157,7 @@ You can show your logs in the test output by passing `--nocapture` (i.e. `cargo
## 5. Documentation <a id="chapter-005"></a>
Be sure to [check out our old wiki](https://openethereum.github.io/wiki/) for more information.
Be sure to [check out our wiki](https://openethereum.github.io/) for more information.
### Viewing documentation for OpenEthereum packages

View File

@ -13,13 +13,11 @@ serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
rustc-hex = "1.0"
tiny-keccak = "1.4"
time = "0.1.34"
itertools = "0.5"
parking_lot = "0.7"
parity-crypto = "0.3.0"
ethereum-types = "0.4"
dir = { path = "../../util/dir" }
smallvec = "0.6"
parity-wordlist = "1.3"
tempdir = "0.3"

View File

@ -18,7 +18,6 @@
#![warn(missing_docs)]
extern crate dir;
extern crate itertools;
extern crate libc;
extern crate parking_lot;
@ -29,7 +28,6 @@ extern crate serde_json;
extern crate smallvec;
extern crate tempdir;
extern crate time;
extern crate tiny_keccak;
extern crate ethereum_types;
extern crate ethkey as _ethkey;

View File

@ -34,7 +34,7 @@ use common_types::{
},
header::{ExtendedHeader, Header},
log_entry::{LocalizedLogEntry, LogEntry},
receipt::Receipt,
receipt::TypedReceipt,
transaction::LocalizedTransaction,
tree_route::TreeRoute,
view,
@ -460,13 +460,13 @@ impl BlockProvider for BlockChain {
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
assert!(false);
}
let mut log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
let mut log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.receipt().logs.len());
let receipts_len = receipts.len();
hashes.reverse();
receipts.reverse();
receipts.into_iter()
.map(|receipt| receipt.logs)
.map(|receipt| receipt.receipt().logs.clone())
.zip(hashes)
.enumerate()
.flat_map(move |(index, (mut logs, tx_hash))| {
@ -749,7 +749,7 @@ impl BlockChain {
}
if hash != bc.genesis_hash() {
trace!("First block calculated: {:?}", hash);
info!("First new 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?");
@ -895,7 +895,7 @@ impl BlockChain {
&self,
batch: &mut DBTransaction,
block: encoded::Block,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
parent_td: Option<U256>,
is_best: bool,
is_ancient: bool,
@ -1265,7 +1265,7 @@ impl BlockChain {
&self,
batch: &mut DBTransaction,
block: encoded::Block,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
extras: ExtrasInsert,
) -> ImportRoute {
let parent_hash = block.header_view().parent_hash();
@ -1283,7 +1283,7 @@ impl BlockChain {
&self,
batch: &mut DBTransaction,
block: encoded::Block,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
route: TreeRoute,
extras: ExtrasInsert,
) -> ImportRoute {
@ -1476,7 +1476,7 @@ impl BlockChain {
}
}
/// Apply pending insertion updates
/// t_nb 9.12 commit changed to become current greatest by applying 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();
@ -1659,7 +1659,7 @@ impl BlockChain {
/// This function returns modified block receipts.
fn prepare_block_receipts_update(
&self,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
info: &BlockInfo,
) -> HashMap<H256, BlockReceipts> {
let mut block_receipts = HashMap::new();
@ -1921,8 +1921,8 @@ mod tests {
use crate::generator::{BlockBuilder, BlockGenerator, BlockOptions};
use common_types::{
receipt::{Receipt, TransactionOutcome},
transaction::{Action, Transaction},
receipt::{LegacyReceipt, TransactionOutcome, TypedReceipt},
transaction::{Action, Transaction, TypedTransaction},
};
use ethkey::Secret;
use keccak_hash::keccak;
@ -1975,7 +1975,7 @@ mod tests {
db: &Arc<dyn BlockChainDB>,
bc: &BlockChain,
block: encoded::Block,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
) -> ImportRoute {
insert_block_commit(db, bc, block, receipts, true)
}
@ -1984,7 +1984,7 @@ mod tests {
db: &Arc<dyn BlockChainDB>,
bc: &BlockChain,
block: encoded::Block,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
commit: bool,
) -> ImportRoute {
let mut batch = db.key_value().transaction();
@ -2000,7 +2000,7 @@ mod tests {
batch: &mut DBTransaction,
bc: &BlockChain,
block: encoded::Block,
receipts: Vec<Receipt>,
receipts: Vec<TypedReceipt>,
) -> ImportRoute {
let fork_choice = {
let header = block.header_view();
@ -2157,7 +2157,7 @@ mod tests {
#[test]
fn test_fork_transaction_addresses() {
let t1 = Transaction {
let t1 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2166,7 +2166,7 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let t1_hash = t1.hash();
@ -2211,7 +2211,7 @@ mod tests {
#[test]
fn test_overwriting_transaction_addresses() {
let t1 = Transaction {
let t1 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2220,10 +2220,10 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let t2 = Transaction {
let t2 = TypedTransaction::Legacy(Transaction {
nonce: 1.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2232,10 +2232,10 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let t3 = Transaction {
let t3 = TypedTransaction::Legacy(Transaction {
nonce: 2.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2244,7 +2244,7 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let genesis = BlockBuilder::genesis();
@ -2509,7 +2509,7 @@ mod tests {
#[test]
fn test_logs() {
let t1 = Transaction {
let t1 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2518,9 +2518,9 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let t2 = Transaction {
let t2 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2529,9 +2529,9 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let t3 = Transaction {
let t3 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2540,9 +2540,9 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let t4 = Transaction {
let t4 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -2551,7 +2551,7 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex()
.unwrap(),
}
})
.sign(&secret(), None);
let tx_hash1 = t1.hash();
let tx_hash2 = t2.hash();
@ -2580,7 +2580,7 @@ mod tests {
&bc,
b1.last().encoded(),
vec![
Receipt {
TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
@ -2596,8 +2596,8 @@ mod tests {
data: vec![2],
},
],
},
Receipt {
}),
TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
@ -2606,14 +2606,14 @@ mod tests {
topics: vec![],
data: vec![3],
}],
},
}),
],
);
insert_block(
&db,
&bc,
b2.last().encoded(),
vec![Receipt {
vec![TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
@ -2622,13 +2622,13 @@ mod tests {
topics: vec![],
data: vec![4],
}],
}],
})],
);
insert_block(
&db,
&bc,
b3.last().encoded(),
vec![Receipt {
vec![TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
@ -2637,7 +2637,7 @@ mod tests {
topics: vec![],
data: vec![5],
}],
}],
})],
);
// when

View File

@ -22,17 +22,16 @@ use std::collections::VecDeque;
use common_types::{
encoded,
header::Header,
transaction::{Action, SignedTransaction, Transaction},
transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
view,
views::BlockView,
};
use keccak_hash::keccak;
use rlp::encode;
use rlp_derive::RlpEncodable;
use rlp::{encode, RlpStream};
use triehash_ethereum::ordered_trie_root;
/// Helper structure, used for encoding blocks.
#[derive(Default, Clone, RlpEncodable)]
#[derive(Default, Clone)]
pub struct Block {
/// Block header
pub header: Header,
@ -42,6 +41,15 @@ pub struct Block {
pub uncles: Vec<Header>,
}
impl rlp::Encodable for Block {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.header);
SignedTransaction::rlp_append_list(s, &self.transactions);
s.append_list(&self.uncles);
}
}
impl Block {
/// Get a copy of the header
#[inline]
@ -154,14 +162,14 @@ impl BlockBuilder {
let data = std::iter::repeat_with(|| rand::random::<u8>())
.take(data_len as usize)
.collect::<Vec<_>>();
Transaction {
TypedTransaction::Legacy(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);
@ -205,7 +213,7 @@ impl BlockBuilder {
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));
let transactions_root = ordered_trie_root(transactions.iter().map(|tx| tx.encode()));
block.header.set_parent_hash(parent_hash);
block.header.set_number(block_number);

View File

@ -22,4 +22,5 @@ eth_pairings = { git = "https://github.com/matter-labs/eip1962.git", default-fea
[dev-dependencies]
hex-literal = "0.2.1"
rustc-hex = "1.0"
maplit = "1.0.2"

View File

@ -81,6 +81,7 @@ enum Pricing {
Blake2F(Blake2FPricer),
Linear(Linear),
Modexp(ModexpPricer),
Modexp2565(Modexp2565Pricer),
Bls12Pairing(Bls12PairingPricer),
Bls12ConstOperations(Bls12ConstOperations),
Bls12MultiexpG1(Bls12MultiexpPricerG1),
@ -95,6 +96,7 @@ impl Pricer for Pricing {
Pricing::Blake2F(inner) => inner.cost(input),
Pricing::Linear(inner) => inner.cost(input),
Pricing::Modexp(inner) => inner.cost(input),
Pricing::Modexp2565(inner) => inner.cost(input),
Pricing::Bls12Pairing(inner) => inner.cost(input),
Pricing::Bls12ConstOperations(inner) => inner.cost(input),
Pricing::Bls12MultiexpG1(inner) => inner.cost(input),
@ -116,6 +118,10 @@ struct ModexpPricer {
divisor: u64,
}
/// The EIP2565 pricing model of modular exponentiation.
#[derive(Debug)]
struct Modexp2565Pricer {}
impl Pricer for Linear {
fn cost(&self, input: &[u8]) -> U256 {
U256::from(self.base) + U256::from(self.word) * U256::from((input.len() + 31) / 32)
@ -156,6 +162,28 @@ impl Pricer for AltBn128PairingPricer {
impl Pricer for ModexpPricer {
fn cost(&self, input: &[u8]) -> U256 {
let (base_len, exp_len, exp_low, mod_len) = Self::parse_input(input);
if let Some(cost) = Self::check_input_boundaries(&base_len, &exp_len, &mod_len) {
return cost;
}
let (base_len, exp_len, mod_len) =
(base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64());
let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low);
let m = max(mod_len, base_len);
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 {
pub fn parse_input(input: &[u8]) -> (U256, U256, U256, U256) {
let mut reader = input.chain(io::repeat(0));
let mut buf = [0; 32];
@ -166,22 +194,12 @@ impl Pricer for ModexpPricer {
.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();
let base_len_u256 = read_len();
let exp_len_u256 = read_len();
let mod_len_u256 = read_len();
if mod_len.is_zero() && base_len.is_zero() {
return U256::zero();
}
let (base_len, exp_len) = (base_len_u256.low_u64(), exp_len_u256.low_u64());
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()
@ -195,17 +213,21 @@ impl Pricer for ModexpPricer {
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()
(base_len_u256, exp_len_u256, exp_low, mod_len_u256)
}
pub fn check_input_boundaries(base_len: &U256, exp_len: &U256, mod_len: &U256) -> Option<U256> {
if mod_len.is_zero() && base_len.is_zero() {
return Some(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 Some(U256::max_value());
}
None
}
}
impl ModexpPricer {
fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 {
let bit_index = if exp_low.is_zero() {
0
@ -228,6 +250,53 @@ impl ModexpPricer {
}
}
impl Pricer for Modexp2565Pricer {
fn cost(&self, input: &[u8]) -> U256 {
fn bit_length(a: &U256) -> u64 {
let mut bit_no = 255u64;
while bit_no > 0 {
if a.bit(bit_no as usize) {
return 1 + bit_no as u64;
}
bit_no -= 1;
}
0
}
fn calculate_multiplication_complexity(base_len: u64, modulus_len: u64) -> f64 {
let max_len = std::cmp::max(base_len, modulus_len);
let words = (max_len as f64 / 8f64).ceil();
words.powi(2)
}
fn calculate_iteration_count(exponent_len: u64, exponent_low: &U256) -> u64 {
let mut iteration_count = 0;
if exponent_len <= 32 && exponent_low.is_zero() {
iteration_count = 0
} else if exponent_len <= 32 {
iteration_count = bit_length(exponent_low) - 1
} else if exponent_len > 32 {
iteration_count = (8 * (exponent_len - 32)) + bit_length(exponent_low) - 1
};
std::cmp::max(iteration_count, 1)
}
let (base_len, exp_len, exp_low, mod_len) = ModexpPricer::parse_input(input);
if let Some(cost) = ModexpPricer::check_input_boundaries(&base_len, &exp_len, &mod_len) {
return cost;
}
let (base_len, exp_len, mod_len) =
(base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64());
let multiplication_complexity = calculate_multiplication_complexity(base_len, mod_len);
let iteration_count = calculate_iteration_count(exp_len, &exp_low);
let computed = (multiplication_complexity * iteration_count as f64 / 3f64).floor() as u64;
U256::from(std::cmp::max(200, computed))
}
}
/// Bls12 pairing price
#[derive(Debug, Copy, Clone)]
struct Bls12PairingPrice {
@ -527,6 +596,9 @@ impl From<ethjson::spec::builtin::Pricing> for Pricing {
exp.divisor
},
}),
ethjson::spec::builtin::Pricing::Modexp2565(_) => {
Pricing::Modexp2565(Modexp2565Pricer {})
}
ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => {
Pricing::AltBn128Pairing(AltBn128PairingPricer {
price: AltBn128PairingPrice {
@ -1286,7 +1358,8 @@ impl Bn128Pairing {
mod tests {
use super::{
modexp as me, BTreeMap, Bls12ConstOperations, Bls12PairingPrice, Bls12PairingPricer,
Builtin, EthereumBuiltin, FromStr, Implementation, Linear, ModexpPricer, Pricing,
Builtin, EthereumBuiltin, FromStr, Implementation, Linear, Modexp2565Pricer, ModexpPricer,
Pricer, Pricing,
};
use ethereum_types::U256;
use ethjson::spec::builtin::{
@ -1298,6 +1371,7 @@ mod tests {
use maplit::btreemap;
use num::{BigUint, One, Zero};
use parity_bytes::BytesRef;
use rustc_hex::FromHex;
use std::convert::TryFrom;
#[test]
@ -2587,4 +2661,47 @@ mod tests {
}
}
}
#[test]
fn modexp_test_pricing() {
let testvectors = vec![
("modexp_nagydani_1_square",204,200,"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b"),
("modexp_nagydani_1_qube",204,200,"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b"),
("modexp_nagydani_1_pow0x10001",3276,341,"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b"),
("modexp_nagydani_2_square",665,200,"000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087"),
("modexp_nagydani_2_qube",665,200,"000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087"),
("modexp_nagydani_2_pow0x10001",10649,1365,"000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087"),
("modexp_nagydani_3_square",1894,341,"000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d"),
("modexp_nagydani_3_qube",1894,341,"000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d"),
("modexp_nagydani_3_pow0x10001",30310,5461,"000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d"),
("modexp_nagydani_4_square",5580,1365,"000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f"),
("modexp_nagydani_4_qube",5580,1365,"000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f"),
("modexp_nagydani_4_pow0x10001",89292,21845,"000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f"),
("modexp_nagydani_5_square",17868,5461,"000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad"),
("modexp_nagydani_5_qube",17868,5461,"000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad"),
("modexp_nagydani_5_pow0x10001",285900,87381,"000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad"),
];
let eip198pricer = ModexpPricer { divisor: 20 };
let eip2565pricer = Modexp2565Pricer {};
for (name, eip198gas, eip2565gas, input) in testvectors {
let input = input.from_hex().unwrap();
assert_eq!(
eip198pricer.cost(&input).as_u64(),
U256::from(eip198gas).low_u64(),
"eip198 gas estimates not equal for {}",
name
);
assert_eq!(
eip2565pricer.cost(&input).as_u64(),
U256::from(eip2565gas).low_u64(),
"eip2565 gas estimates not equal for {}",
name
);
}
}
}

View File

@ -18,12 +18,14 @@
use std::{io::Write, ops};
use common_types::{engines::epoch::Transition as EpochTransition, receipt::Receipt, BlockNumber};
use common_types::{
engines::epoch::Transition as EpochTransition, receipt::TypedReceipt, BlockNumber,
};
use ethereum_types::{H256, H264, U256};
use heapsize::HeapSizeOf;
use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
use rlp;
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
use rlp_derive::{RlpDecodable, RlpEncodable};
use crate::db::Key;
@ -235,15 +237,27 @@ impl HeapSizeOf for TransactionAddress {
}
/// Contains all block receipts.
#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)]
#[derive(Clone)]
pub struct BlockReceipts {
/// Block receipts
pub receipts: Vec<Receipt>,
pub receipts: Vec<TypedReceipt>,
}
impl rlp::Encodable for BlockReceipts {
fn rlp_append(&self, s: &mut rlp::RlpStream) {
TypedReceipt::rlp_append_list(s, &self.receipts);
}
}
impl rlp::Decodable for BlockReceipts {
fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
Ok(BlockReceipts::new(TypedReceipt::decode_rlp_list(rlp)?))
}
}
impl BlockReceipts {
/// Create new block receipts wrapper.
pub fn new(receipts: Vec<Receipt>) -> Self {
pub fn new(receipts: Vec<TypedReceipt>) -> Self {
BlockReceipts { receipts: receipts }
}
}

View File

@ -15,6 +15,7 @@ vm = { path = "../vm" }
keccak-hash = "0.1"
parking_lot = "0.7"
memory-cache = { path = "../../util/memory-cache" }
ethcore-builtin = { path = "../builtin" }
num-bigint = "0.2"
[dev-dependencies]

View File

@ -33,7 +33,7 @@ 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 std::{collections::BTreeMap, str::FromStr, sync::Arc};
use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result};
criterion_group!(

View File

@ -15,9 +15,10 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use super::u256_to_address;
use ethereum_types::{H256, U256};
use ethereum_types::{Address, H256, U256};
use std::cmp;
use super::stack::VecStack;
use evm;
use instructions::{self, Instruction, InstructionInfo};
use interpreter::stack::Stack;
@ -115,26 +116,38 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
ext: &dyn vm::Ext,
instruction: Instruction,
info: &InstructionInfo,
stack: &dyn Stack<U256>,
stack: &VecStack<U256>,
current_address: &Address,
current_mem_size: usize,
) -> vm::Result<InstructionRequirements<Gas>> {
let schedule = ext.schedule();
let tier = info.tier.idx();
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let accessed_addresses_gas = |addr: &Address, cold_cost: usize| -> Gas {
if ext.al_contains_address(addr) {
schedule.warm_storage_read_cost.into()
} else {
cold_cost.into()
}
};
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 key = H256::from(stack.peek(0));
let newval = stack.peek(1);
let val = U256::from(&*ext.storage_at(&address)?);
let val = U256::from(&*ext.storage_at(&key)?);
let is_cold = !ext.al_contains_storage_key(current_address, &key);
let gas = if schedule.eip1283 {
let orig = U256::from(&*ext.initial_storage_at(&address)?);
calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval)
let orig = U256::from(&*ext.initial_storage_at(&key)?);
calculate_eip1283_eip2929_sstore_gas(schedule, is_cold, &orig, &val, &newval)
} else {
if val.is_zero() && !newval.is_zero() {
schedule.sstore_set_gas
@ -144,17 +157,40 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
schedule.sstore_reset_gas
}
};
Request::Gas(Gas::from(gas))
Request::Gas(gas.into())
}
instructions::SLOAD => {
let key = H256::from(stack.peek(0));
let gas = if ext.al_is_enabled() {
if ext.al_contains_storage_key(current_address, &key) {
schedule.warm_storage_read_cost
} else {
schedule.cold_sload_cost
}
} else {
schedule.sload_gas
};
Request::Gas(gas.into())
}
instructions::BALANCE => {
let address = u256_to_address(stack.peek(0));
Request::Gas(accessed_addresses_gas(&address, schedule.balance_gas))
}
instructions::EXTCODESIZE => {
let address = u256_to_address(stack.peek(0));
Request::Gas(accessed_addresses_gas(&address, schedule.extcodesize_gas))
}
instructions::EXTCODEHASH => {
let address = u256_to_address(stack.peek(0));
Request::Gas(accessed_addresses_gas(&address, schedule.extcodehash_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
@ -164,6 +200,13 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
}
if !ext.al_contains_address(&address) {
// EIP2929 If the ETH recipient of a SELFDESTRUCT is not in accessed_addresses
// (regardless of whether or not the amount sent is nonzero),
// charge an additional COLD_ACCOUNT_ACCESS_COST on top of the existing gas costs,
gas = Gas::from(gas.as_usize() + schedule.cold_account_access_cost);
}
Request::Gas(gas)
}
instructions::MSTORE | instructions::MLOAD => {
@ -189,11 +232,15 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
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::EXTCODECOPY => {
let address = u256_to_address(stack.peek(0));
let gas = accessed_addresses_gas(&address, schedule.extcodecopy_base_gas);
Request::GasMemCopy(
gas,
mem_needed(stack.peek(1), stack.peek(3))?,
Gas::from_u256(*stack.peek(3))?,
)
}
instructions::LOG0
| instructions::LOG1
| instructions::LOG2
@ -218,6 +265,8 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
);
let address = u256_to_address(stack.peek(1));
gas = accessed_addresses_gas(&address, gas.as_usize());
let is_value_transfer = !stack.peek(2).is_zero();
if instruction == instructions::CALL
@ -238,7 +287,10 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
Request::GasMemProvide(gas, mem, Some(requested))
}
instructions::DELEGATECALL | instructions::STATICCALL => {
let gas = Gas::from(schedule.call_gas);
let mut gas = Gas::from(schedule.call_gas);
let address = u256_to_address(stack.peek(1));
gas = accessed_addresses_gas(&address, gas.as_usize());
let mem = cmp::max(
mem_needed(stack.peek(4), stack.peek(5))?,
mem_needed(stack.peek(2), stack.peek(3))?,
@ -389,41 +441,39 @@ fn to_word_size<Gas: evm::CostType>(value: Gas) -> (Gas, bool) {
}
#[inline]
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(
fn calculate_eip1283_eip2929_sstore_gas<Gas: evm::CostType>(
schedule: &Schedule,
is_cold: bool,
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.
Gas::from(
if current == new {
// 1. If current value equals new value (this is a no-op).
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.
}
})
} 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.
schedule.sstore_set_gas
} else {
// 2.1.2. Otherwise.
schedule.sstore_reset_gas
}
} else {
// 2.2. If original value does not equal current value (this storage slot is dirty).
schedule.sload_gas
}
} + if is_cold {
// EIP2929 SSTORE changes section
schedule.cold_sload_cost
} else {
0
},
)
}
pub fn handle_eip1283_sstore_clears_refund(

View File

@ -398,8 +398,14 @@ impl<Cost: CostType> Interpreter<Cost> {
.gasometer
.as_mut()
.expect(GASOMETER_PROOF)
.requirements(ext, instruction, info, &self.stack, self.mem.size())
{
.requirements(
ext,
instruction,
info,
&self.stack,
&self.params.address,
self.mem.size(),
) {
Ok(t) => t,
Err(e) => return InterpreterResult::Done(Err(e)),
};
@ -709,6 +715,15 @@ impl<Cost: CostType> Interpreter<Cost> {
return Ok(InstructionResult::UnusedGas(create_gas));
}
let contract_address = {
let contract_code = self.mem.read_slice(init_off, init_size);
ext.calc_address(contract_code, address_scheme)
};
if let Some(contract_address) = contract_address {
ext.al_insert_address(contract_address);
}
let contract_code = self.mem.read_slice(init_off, init_size);
let create_result = ext.create(
@ -777,6 +792,8 @@ impl<Cost: CostType> Interpreter<Cost> {
))
.0;
ext.al_insert_address(code_address);
// Get sender & receive addresses, check if we have balance
let (sender_address, receive_address, has_balance, call_type) = match instruction {
instructions::CALL => {
@ -903,8 +920,9 @@ impl<Cost: CostType> Interpreter<Cost> {
return Ok(InstructionResult::StopExecution);
}
instructions::SUICIDE => {
let address = self.stack.pop_back();
ext.suicide(&u256_to_address(&address))?;
let address = u256_to_address(&self.stack.pop_back());
ext.al_insert_address(address.clone());
ext.suicide(&address)?;
return Ok(InstructionResult::StopExecution);
}
instructions::LOG0
@ -991,15 +1009,17 @@ impl<Cost: CostType> Interpreter<Cost> {
let key = H256::from(&self.stack.pop_back());
let word = U256::from(&*ext.storage_at(&key)?);
self.stack.push(word);
ext.al_insert_storage_key(self.params.address, key);
}
instructions::SSTORE => {
let address = H256::from(&self.stack.pop_back());
let key = H256::from(&self.stack.pop_back());
let val = self.stack.pop_back();
let current_val = U256::from(&*ext.storage_at(&address)?);
let current_val = U256::from(&*ext.storage_at(&key)?);
// Increase refund for clear
if ext.schedule().eip1283 {
let original_val = U256::from(&*ext.initial_storage_at(&address)?);
let original_val = U256::from(&*ext.initial_storage_at(&key)?);
gasometer::handle_eip1283_sstore_clears_refund(
ext,
&original_val,
@ -1012,7 +1032,8 @@ impl<Cost: CostType> Interpreter<Cost> {
ext.add_sstore_refund(sstore_clears_schedule);
}
}
ext.set_storage(address, H256::from(&val))?;
ext.set_storage(key, H256::from(&val))?;
ext.al_insert_storage_key(self.params.address, key);
}
instructions::PC => {
self.stack.push(U256::from(self.reader.position - 1));
@ -1031,6 +1052,7 @@ impl<Cost: CostType> Interpreter<Cost> {
let address = u256_to_address(&self.stack.pop_back());
let balance = ext.balance(&address)?;
self.stack.push(balance);
ext.al_insert_address(address);
}
instructions::CALLER => {
self.stack.push(address_to_u256(self.params.sender.clone()));
@ -1068,11 +1090,15 @@ impl<Cost: CostType> Interpreter<Cost> {
instructions::EXTCODESIZE => {
let address = u256_to_address(&self.stack.pop_back());
let len = ext.extcodesize(&address)?.unwrap_or(0);
ext.al_insert_address(address);
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);
ext.al_insert_address(address);
self.stack.push(U256::from(hash));
}
instructions::CALLDATACOPY => {
@ -1108,6 +1134,7 @@ impl<Cost: CostType> Interpreter<Cost> {
&mut self.stack,
code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[]),
);
ext.al_insert_address(address);
}
instructions::GASPRICE => {
self.stack.push(self.params.gas_price.clone());

View File

@ -17,6 +17,7 @@
//! Ethereum virtual machine.
extern crate bit_set;
extern crate ethcore_builtin as builtin;
extern crate ethereum_types;
extern crate heapsize;
extern crate keccak_hash as hash;

View File

@ -1554,6 +1554,113 @@ fn test_sar(factory: super::Factory) {
);
}
// from https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a
evm_test! {test_access_list_ext_at_precompiles: test_access_list_ext_at_precompiles_int}
fn test_access_list_ext_at_precompiles(factory: super::Factory) {
// 6001 3f 50
// 6002 3b 50
// 6003 31 50
// 60f1 3f 50
// 60f2 3b 50
// 60f3 31 50
// 60f2 3f 50
// 60f3 3b 50
// 60f1 31 50
// 32 31 50
// 30 31 50
// 00
let code = hex!(
"60013f5060023b506003315060f13f5060f23b5060f3315060f23f5060f33b5060f1315032315030315000"
)
.to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(8653);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[
Address::from("0x0000000000000000000000000000000000000001"),
Address::from("0x0000000000000000000000000000000000000002"),
Address::from("0x0000000000000000000000000000000000000003"),
],
);
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_access_list_extcodecopy_twice: test_access_list_extcodecopy_twice_int}
fn test_access_list_extcodecopy_twice(factory: super::Factory) {
let code = hex!("60006000600060ff3c60006000600060ff3c600060006000303c").to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(2835);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[],
);
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_access_list_sload_sstore: test_access_list_sload_sstore_int}
fn test_access_list_sload_sstore(factory: super::Factory) {
// 6001 54 50 sload( 0x1) pop
// 6011 6001 55 sstore(loc: 0x01, val:0x11) 20000
// 6011 6002 55 sstore(loc: 0x02, val:0x11) 20000 + 2100
// 6011 6002 55 sstore(loc: 0x02, val:0x11) 100
// 6002 54 sload(0x2)
// 6001 54 sload(0x1)
let code = hex!("60015450 6011600155 6011600255 6011600255 600254 600154").to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(44529);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[],
);
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_access_list_cheap_expensive_cheap: test_access_list_cheap_expensive_cheap_int}
fn test_access_list_cheap_expensive_cheap(factory: super::Factory) {
let code =
hex!("60008080808060046000f15060008080808060ff6000f15060008080808060ff6000fa50").to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(2869);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[Address::from("0x0000000000000000000000000000000000000004")],
);
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));
}
fn push_two_pop_one_constantinople_test(
factory: &super::Factory,
opcode: u8,

View File

@ -1,5 +1,5 @@
{
"name": "Istanbul (test)",
"name": "Berlin (test)",
"engine": {
"Ethash": {
"params": {
@ -17,11 +17,11 @@
},
"params": {
"gasLimitBoundDivisor": "0x0400",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"registrar": "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"networkID": "0x1",
"maxCodeSize": 24576,
"maxCodeSizeTransition": "0x0",
"eip150Transition": "0x0",
@ -44,7 +44,6 @@
"eip1884Transition": "0x0",
"eip2028Transition": "0x0",
"eip2315Transition": "0x0"
},
"genesis": {
"seal": {
@ -61,21 +60,83 @@
"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 } } } },
"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": {
"0": {
"price" : { "modexp2565": { } }
}
}
}
},
"0000000000000000000000000000000000000006": {
"builtin": {
"name": "alt_bn128_add",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
"price": {
"alt_bn128_const_operations": {
"price": 500
}
}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 150 }}
"price": {
"alt_bn128_const_operations": {
"price": 150
}
}
}
}
}
@ -85,11 +146,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
"price": {
"alt_bn128_const_operations": {
"price": 40000
}
}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
"price": {
"alt_bn128_const_operations": {
"price": 6000
}
}
}
}
}
@ -99,11 +168,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
"price": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
"price": {
"alt_bn128_pairing": {
"base": 45000,
"pair": 34000
}
}
}
}
}
@ -124,7 +203,9 @@
"name": "bls12_381_g1_add",
"activate_at": "0",
"pricing": {
"bls12_const_operations": { "price": 600 }
"bls12_const_operations": {
"price": 600
}
}
}
},
@ -133,7 +214,9 @@
"name": "bls12_381_g1_mul",
"activate_at": "0",
"pricing": {
"bls12_const_operations": { "price": 12000 }
"bls12_const_operations": {
"price": 12000
}
}
}
},
@ -142,7 +225,9 @@
"name": "bls12_381_g1_multiexp",
"activate_at": "0",
"pricing": {
"bls12_g1_multiexp": { "base": 12000 }
"bls12_g1_multiexp": {
"base": 12000
}
}
}
},
@ -151,7 +236,9 @@
"name": "bls12_381_g2_add",
"activate_at": "0",
"pricing": {
"bls12_const_operations": { "price": 4500 }
"bls12_const_operations": {
"price": 4500
}
}
}
},
@ -160,7 +247,9 @@
"name": "bls12_381_g2_mul",
"activate_at": "0",
"pricing": {
"bls12_const_operations": { "price": 55000 }
"bls12_const_operations": {
"price": 55000
}
}
}
},
@ -169,7 +258,9 @@
"name": "bls12_381_g2_multiexp",
"activate_at": "0",
"pricing": {
"bls12_g2_multiexp": { "base": 55000 }
"bls12_g2_multiexp": {
"base": 55000
}
}
}
},
@ -178,7 +269,10 @@
"name": "bls12_381_pairing",
"activate_at": "0",
"pricing": {
"bls12_pairing": { "base": 115000, "pair": 23000 }
"bls12_pairing": {
"base": 115000,
"pair": 23000
}
}
}
},
@ -187,7 +281,9 @@
"name": "bls12_381_fp_to_g1",
"activate_at": "0",
"pricing": {
"bls12_const_operations": { "price": 5500 }
"bls12_const_operations": {
"price": 5500
}
}
}
},
@ -196,9 +292,11 @@
"name": "bls12_381_fp2_to_g2",
"activate_at": "0",
"pricing": {
"bls12_const_operations": { "price": 110000 }
"bls12_const_operations": {
"price": 110000
}
}
}
}
}
}
}

View File

@ -198,7 +198,10 @@
"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"
"enode://5f7d0794c464b2fcd514d41e16e4b535a98ac792a71ca9667c7cef35595dc34c9a1b793c0622554cf87f34006942abb526af7d2e37d715ac32ed02170556cce2@51.161.101.207:30303",
"enode://8e78d404250e33c0199d6e53696cabe14ea2b57d534f60be4c6bb4550d006a358806dd10e2994bc11287194cea904c6cb37eda47aa84e2367149f0c65b037288@51.79.213.98:30303",
"enode://cb886f1d2f0d9d686822fbdeba52f87d7b36b9982dda0f320496afe1d1fd4954bd5ee3f0066319dd6ea65c42b69168398ce79efcb984b64ab7242c97d152eb71@51.222.117.211:30303",
"enode://4a403f33a3fa6ae0cd790a91cf967db3e23e6d298158a7e30017c9c665b70e451e2188d7182fce743bfa64f91e04c5791c245cbf4d3b159931cf2cf0bb9b5ec5@51.161.101.207:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000001": {

View File

@ -42,7 +42,8 @@
"eip211Transition": 5067000,
"eip214Transition": 5067000,
"eip658Transition": 5067000,
"wasmActivationTransition": 10
"wasmActivationTransition": 10,
"wasmDisableTransition": 200
},
"genesis": {
"seal": {

View File

@ -0,0 +1,204 @@
{
"name": "Yolo (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",
"eip2929Transition": "0x0",
"eip2930Transition": "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": {
"0": {
"price" : { "modexp2565": { } }
}
}
}
},
"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
}
}
}
}
}
}

View File

@ -48,10 +48,10 @@ use verification::PreverifiedBlock;
use vm::{EnvInfo, LastHashes};
use hash::keccak;
use rlp::{encode_list, Encodable, RlpStream};
use rlp::{encode_list, RlpStream};
use types::{
header::{ExtendedHeader, Header},
receipt::{Receipt, TransactionOutcome},
receipt::{TransactionOutcome, TypedReceipt},
transaction::{Error as TransactionError, SignedTransaction},
};
@ -99,7 +99,7 @@ pub struct ExecutedBlock {
/// Uncles.
pub uncles: Vec<Header>,
/// Transaction receipts.
pub receipts: Vec<Receipt>,
pub receipts: Vec<TypedReceipt>,
/// Hashes of already executed transactions.
pub transactions_set: HashSet<H256>,
/// Underlaying state.
@ -161,7 +161,7 @@ pub trait Drain {
}
impl<'x> OpenBlock<'x> {
/// Create a new `OpenBlock` ready for transaction pushing.
/// t_nb 8.1 Create a new `OpenBlock` ready for transaction pushing.
pub fn new<'a, I: IntoIterator<Item = ExtendedHeader>>(
engine: &'x dyn EthEngine,
factories: Factories,
@ -176,6 +176,8 @@ impl<'x> OpenBlock<'x> {
ancestry: I,
) -> Result<Self, Error> {
let number = parent.number() + 1;
// t_nb 8.1.1 get parent StateDB.
let state = State::from_existing(
db,
parent.state_root().clone(),
@ -198,14 +200,17 @@ impl<'x> OpenBlock<'x> {
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);
// t_nb 8.1.2 It calculated child gas limits should be.
engine.machine().populate_from_parent(
&mut r.block.header,
parent,
gas_floor_target,
gas_ceil_target,
);
// t_nb 8.1.3 this adds engine specific things
engine.populate_from_parent(&mut r.block.header, parent);
// t_nb 8.1.3 updating last hashes and the DAO fork, for ethash.
engine.machine().on_new_block(&mut r.block)?;
engine.on_new_block(&mut r.block, is_epoch_begin, &mut ancestry.into_iter())?;
@ -222,7 +227,7 @@ impl<'x> OpenBlock<'x> {
self.block.header.set_gas_limit(U256::max_value());
}
/// Add an uncle to the block, if possible.
// t_nb 8.4 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.
@ -248,7 +253,7 @@ impl<'x> OpenBlock<'x> {
&mut self,
t: SignedTransaction,
h: Option<H256>,
) -> Result<&Receipt, Error> {
) -> Result<&TypedReceipt, Error> {
if self.block.transactions_set.contains(&t.hash()) {
return Err(TransactionError::AlreadyImported.into());
}
@ -343,21 +348,25 @@ impl<'x> OpenBlock<'x> {
})
}
/// Turn this into a `LockedBlock`.
/// t_nb 8.5 Turn this into a `LockedBlock`.
pub fn close_and_lock(self) -> Result<LockedBlock, Error> {
let mut s = self;
// t_nb 8.5.1 engine applies block rewards (Ethash and AuRa do.Clique is empty)
s.engine.on_close_block(&mut s.block)?;
// t_nb 8.5.2 commit account changes from cache to tree
s.block.state.commit()?;
// t_nb 8.5.3 fill open block header with all other fields
s.block.header.set_transactions_root(ordered_trie_root(
s.block.transactions.iter().map(|e| e.rlp_bytes()),
s.block.transactions.iter().map(|e| e.encode()),
));
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.receipts.iter().map(|r| r.encode()),
));
s.block
.header
@ -444,7 +453,7 @@ impl LockedBlock {
receipt.outcome = TransactionOutcome::Unknown;
}
self.block.header.set_receipts_root(ordered_trie_root(
self.block.receipts.iter().map(|r| r.rlp_bytes()),
self.block.receipts.iter().map(|r| r.encode()),
));
}
@ -494,7 +503,7 @@ impl SealedBlock {
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);
SignedTransaction::rlp_append_list(&mut block_rlp, &self.block.transactions);
block_rlp.append_list(&self.block.uncles);
block_rlp.out()
}
@ -506,7 +515,7 @@ impl Drain for SealedBlock {
}
}
/// Enact the block given by block header, transactions and uncles
// t_nb 8.0 Enact the block given by block header, transactions and uncles
pub(crate) fn enact(
header: Header,
transactions: Vec<SignedTransaction>,
@ -532,6 +541,7 @@ pub(crate) fn enact(
None
};
// t_nb 8.1 Created new OpenBlock
let mut b = OpenBlock::new(
engine,
factories,
@ -556,17 +566,22 @@ pub(crate) fn enact(
b.block.header.number(), root, env.author, author_balance);
}
// t_nb 8.2 transfer all field from current header to OpenBlock header that we created
b.populate_from(&header);
// t_nb 8.3 execute transactions one by one
b.push_transactions(transactions)?;
// t_nb 8.4 Push uncles to OpenBlock and check if we have more then max uncles
for u in uncles {
b.push_uncle(u)?;
}
// t_nb 8.5 close block
b.close_and_lock()
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
/// t_nb 8.0 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: &dyn EthEngine,

View File

@ -41,7 +41,7 @@ use itertools::Itertools;
use kvdb::{DBTransaction, DBValue, KeyValueDB};
use parking_lot::{Mutex, RwLock};
use rand::OsRng;
use rlp::PayloadInfo;
use rlp::{PayloadInfo, Rlp};
use rustc_hex::FromHex;
use trie::{Trie, TrieFactory, TrieSpec};
use types::{
@ -51,8 +51,11 @@ use types::{
filter::Filter,
header::{ExtendedHeader, Header},
log_entry::LocalizedLogEntry,
receipt::{LocalizedReceipt, Receipt},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, UnverifiedTransaction},
receipt::{LocalizedReceipt, TypedReceipt},
transaction::{
self, Action, LocalizedTransaction, SignedTransaction, TypedTransaction,
UnverifiedTransaction,
},
BlockNumber,
};
use vm::{EnvInfo, LastHashes};
@ -275,7 +278,7 @@ impl Importer {
})
}
/// This is triggered by a message coming from a block queue when the block is ready for insertion
// t_nb 6.0 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) {
@ -315,11 +318,13 @@ impl Importer {
invalid_blocks.insert(hash);
continue;
}
// t_nb 7.0 check and lock block
match self.check_and_lock_block(&bytes, block, client) {
Ok((closed_block, pending)) => {
imported_blocks.push(hash);
let transactions_len = closed_block.transactions.len();
// t_nb 8.0 commit block to db
let route = self.commit_block(
closed_block,
&header,
@ -362,6 +367,7 @@ impl Importer {
if !imported_blocks.is_empty() {
let route = ChainRoute::from(import_results.as_ref());
// t_nb 10 Notify miner about new included block.
if !has_more_blocks_to_import {
self.miner.chain_new_blocks(
client,
@ -373,6 +379,7 @@ impl Importer {
);
}
// t_nb 11 notify rest of system about new block inclusion
client.notify(|notify| {
notify.new_blocks(NewBlocks::new(
imported_blocks.clone(),
@ -389,9 +396,12 @@ impl Importer {
let db = client.db.read();
db.key_value().flush().expect("DB flush failed.");
self.block_queue.resignal_verification();
imported
}
// t_nb 6.0.1 check and lock block,
fn check_and_lock_block(
&self,
bytes: &[u8],
@ -402,13 +412,14 @@ impl Importer {
let header = block.header.clone();
// Check the block isn't so old we won't be able to enact it.
// t_nb 7.1 check if block is older then last pruned block
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
// t_nb 7.2 Check if parent is in chain
let parent = match client.block_header_decoded(BlockId::Hash(*header.parent_hash())) {
Some(h) => h,
None => {
@ -418,7 +429,7 @@ impl Importer {
};
let chain = client.chain.read();
// Verify Block Family
// t_nb 7.3 verify block family
let verify_family_result = self.verifier.verify_block_family(
&header,
&parent,
@ -435,6 +446,7 @@ impl Importer {
bail!(e);
};
// t_nb 7.4 verify block external
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);
@ -442,7 +454,9 @@ impl Importer {
};
// Enact Verified Block
// t_nb 7.5 Get build last hashes. Get parent state db. Get epoch_transition
let last_hashes = client.build_last_hashes(header.parent_hash());
let db = client
.state_db
.read()
@ -452,6 +466,7 @@ impl Importer {
.epoch_transition(parent.number(), *header.parent_hash())
.is_some();
// t_nb 8.0 Block enacting. Execution of transactions.
let enact_result = enact_verified(
block,
engine,
@ -472,7 +487,7 @@ impl Importer {
}
};
// Strip receipts for blocks before validate_receipts_transition,
// t_nb 7.6 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
@ -481,7 +496,7 @@ impl Importer {
locked_block.strip_receipts_outcomes();
}
// Final Verification
// t_nb 7.7 Final Verification. See if block that we created (executed) matches exactly with block that we received.
if let Err(e) = self
.verifier
.verify_block_final(&header, &locked_block.header)
@ -512,9 +527,14 @@ impl Importer {
db: &dyn KeyValueDB,
chain: &BlockChain,
) -> EthcoreResult<()> {
let receipts = ::rlp::decode_list(receipts_bytes);
let receipts = TypedReceipt::decode_rlp_list(&Rlp::new(receipts_bytes))
.unwrap_or_else(|e| panic!("Receipt bytes should be valid: {:?}", e));
let _import_lock = self.import_lock.lock();
if unverified.header.number() >= chain.best_block_header().number() {
panic!("Ancient block number is higher then best block number");
}
{
trace_time!("import_old_block");
// verify the block, passing the chain for updating the epoch verifier.
@ -568,6 +588,7 @@ impl Importer {
let mut batch = DBTransaction::new();
// t_nb 9.1 Gather all ancestry actions. (Used only by AuRa)
let ancestry_actions = self
.engine
.ancestry_actions(&header, &mut chain.ancestry_with_metadata_iter(*parent));
@ -603,24 +624,28 @@ impl Importer {
}
};
// t_nb 9.2 calcuate route between current and latest block.
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");
// t_nb 9.3 Check block total difficulty
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
// t_nb 9.4 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.
// t_nb 9.5 check epoch end signal, potentially generating a proof on the current
// state. Write transition into db.
if let Some(pending) = pending {
chain.insert_pending_transition(&mut batch, header.hash(), pending);
}
// t_nb 9.6 push state to database Transaction. (It calls journal_under from JournalDB)
state
.journal_under(&mut batch, number, hash)
.expect("DB commit failed");
@ -631,6 +656,7 @@ impl Importer {
let AncestryAction::MarkFinalized(a) = ancestry_action;
if a != header.hash() {
// t_nb 9.7 if there are finalized ancester, mark that chainge in block in db. (Used by AuRa)
chain
.mark_finalized(&mut batch, a)
.expect("Engine's ancestry action must be known blocks; qed");
@ -643,6 +669,7 @@ impl Importer {
})
.collect();
// t_nb 9.8 insert block
let route = chain.insert_block(
&mut batch,
block_data,
@ -653,6 +680,7 @@ impl Importer {
},
);
// t_nb 9.9 insert traces (if they are enabled)
client.tracedb.read().import(
&mut batch,
TraceImportRequest {
@ -665,15 +693,22 @@ impl Importer {
);
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
// t_nb 9.10 sync cache
state.sync_cache(&route.enacted, &route.retracted, is_canon);
// Final commit to the DB
// t_nb 9.11 Write Transaction to database (cached)
client.db.read().key_value().write_buffered(batch);
// t_nb 9.12 commit changed to become current greatest by applying pending insertion updates (Sync point)
chain.commit();
// t_nb 9.13 check epoch end. Related only to AuRa and it seems light engine
self.check_epoch_end(&header, &finalized, &chain, client);
// t_nb 9.14 update last hashes. They are build in step 7.5
client.update_last_hashes(&parent, hash);
// t_nb 9.15 prune ancient states
if let Err(e) = client.prune_ancient(state, &chain) {
warn!("Failed to prune ancient state data: {}", e);
}
@ -687,7 +722,7 @@ impl Importer {
&self,
header: &Header,
block_bytes: &[u8],
receipts: &[Receipt],
receipts: &[TypedReceipt],
state_db: &StateDB,
client: &Client,
) -> EthcoreResult<Option<PendingTransition>> {
@ -1096,7 +1131,7 @@ impl Client {
with_call(&call)
}
// prune ancient states until below the memory limit or only the minimum amount remain.
// t_nb 9.15 prune ancient states until below the memory limit or only the minimum amount remain.
fn prune_ancient(
&self,
mut state_db: StateDB,
@ -1136,6 +1171,7 @@ impl Client {
Ok(())
}
// t_nb 9.14 update last hashes. They are build in step 7.5
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) {
@ -1169,15 +1205,27 @@ impl Client {
/// Get a copy of the best block's state.
pub fn latest_state_and_header(&self) -> (State<StateDB>, 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)
let mut nb_tries = 5;
// Here, we are taking latest block and then latest state. If in between those two calls `best` block got prunned app will panic.
// This is something that should not happend often and it is edge case.
// Locking read best_block lock would be more straighforward, but can introduce overlaping locks,
// because of this we are just taking 5 tries to get best state in most cases it will work on first try.
while nb_tries != 0 {
let header = self.best_block_header();
match 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(),
) {
Ok(ret) => return (ret, header),
Err(_) => {
warn!("Couldn't fetch state of best block header: {:?}", header);
nb_tries -= 1;
}
}
}
panic!("Couldn't get latest state in 5 tries");
}
/// Attempt to get a copy of a specific block's final state.
@ -1408,7 +1456,7 @@ impl Client {
data: Bytes,
) -> SignedTransaction {
let from = Address::default();
transaction::Transaction {
TypedTransaction::Legacy(transaction::Transaction {
nonce: self
.nonce(&from, block_id)
.unwrap_or_else(|| self.engine.account_start_nonce(0)),
@ -1417,7 +1465,7 @@ impl Client {
gas_price: U256::default(),
value: U256::default(),
data: data,
}
})
.fake_sign(from)
}
@ -1719,11 +1767,14 @@ impl CallContract for Client {
}
impl ImportBlock for Client {
// t_nb 2.0 import block to client
fn import_block(&self, unverified: Unverified) -> EthcoreResult<H256> {
// t_nb 2.1 check if header hash is known to us.
if self.chain.read().is_known(&unverified.hash()) {
bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain));
}
// t_nb 2.2 check if parent is known
let status = self.block_status(BlockId::Hash(unverified.parent_hash()));
if status == BlockStatus::Unknown {
bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(
@ -1741,14 +1792,16 @@ impl ImportBlock for Client {
None
};
// t_nb 2.3
match self.importer.block_queue.import(unverified) {
Ok(hash) => {
// t_nb 2.4 If block is okay and the queue is empty we propagate the block in a `PriorityTask` to be rebrodcasted
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)
// t_nb 2.5 if block is not okay print error. we only care about block errors (not import errors)
Err((Some(block), EthcoreError(EthcoreErrorKind::Block(err), _))) => {
self.importer
.bad_blocks
@ -1856,7 +1909,7 @@ impl Call for Client {
let exec = |gas| {
let mut tx = t.as_unsigned().clone();
tx.gas = gas;
tx.tx_mut().gas = gas;
let tx = tx.fake_sign(sender);
let mut clone = state.clone();
@ -1887,6 +1940,7 @@ impl Call for Client {
}
}
let lower = t
.tx()
.gas_required(&self.engine.schedule(env_info.number))
.into();
if cond(lower) {
@ -2538,18 +2592,18 @@ impl BlockChainClient for Client {
} else {
self.importer.miner.sensible_gas_price()
};
let transaction = transaction::Transaction {
let transaction = TypedTransaction::Legacy(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))
.sign(transaction.signature_hash(chain_id))
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
self.importer
@ -2569,10 +2623,15 @@ impl IoClient for Client {
self.queue_transactions
.queue(&self.io_channel.read(), len, move |client| {
trace_time!("import_queued_transactions");
let best_block_number = client.best_block_header().number();
let txs: Vec<UnverifiedTransaction> = transactions
.iter()
.filter_map(|bytes| client.engine.decode_transaction(bytes).ok())
.filter_map(|bytes| {
client
.engine
.decode_transaction(bytes, best_block_number)
.ok()
})
.collect();
client.notify(|notify| {
@ -2921,7 +2980,7 @@ impl ProvingBlockChainClient for Client {
_ => return None,
};
env_info.gas_limit = transaction.gas.clone();
env_info.gas_limit = transaction.tx().gas.clone();
let mut jdb = self.state_db.read().journal_db().boxed_clone();
state::prove_transaction_virtual(
@ -3079,7 +3138,7 @@ impl ImportExportBlocks for Client {
fn transaction_receipt(
machine: &::machine::EthereumMachine,
mut tx: LocalizedTransaction,
receipt: Receipt,
receipt: TypedReceipt,
prior_gas_used: U256,
prior_no_of_logs: usize,
) -> LocalizedReceipt {
@ -3088,27 +3147,31 @@ fn transaction_receipt(
let block_hash = tx.block_hash;
let block_number = tx.block_number;
let transaction_index = tx.transaction_index;
let transaction_type = tx.tx_type();
let receipt = receipt.receipt().clone();
LocalizedReceipt {
from: sender,
to: match tx.action {
to: match tx.tx().action {
Action::Create => None,
Action::Call(ref address) => Some(address.clone().into()),
},
transaction_hash: transaction_hash,
transaction_index: transaction_index,
transaction_type: transaction_type,
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 {
contract_address: match tx.tx().action {
Action::Call(_) => None,
Action::Create => Some(
contract_address(
machine.create_address_scheme(block_number),
&sender,
&tx.nonce,
&tx.data,
&tx.tx().nonce,
&tx.tx().data,
)
.0,
),
@ -3128,7 +3191,7 @@ fn transaction_receipt(
})
.collect(),
log_bloom: receipt.log_bloom,
outcome: receipt.outcome,
outcome: receipt.outcome.clone(),
}
}
@ -3424,8 +3487,8 @@ mod tests {
use hash::keccak;
use types::{
log_entry::{LocalizedLogEntry, LogEntry},
receipt::{LocalizedReceipt, Receipt, TransactionOutcome},
transaction::{Action, LocalizedTransaction, Transaction},
receipt::{LegacyReceipt, LocalizedReceipt, TransactionOutcome, TypedReceipt},
transaction::{Action, LocalizedTransaction, Transaction, TypedTransaction},
};
// given
@ -3437,14 +3500,14 @@ mod tests {
let block_hash = 5.into();
let state_root = 99.into();
let gas_used = 10.into();
let raw_tx = Transaction {
let raw_tx = TypedTransaction::Legacy(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(),
@ -3465,12 +3528,12 @@ mod tests {
data: vec![],
},
];
let receipt = Receipt {
let receipt = TypedReceipt::Legacy(LegacyReceipt {
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);
@ -3480,12 +3543,13 @@ mod tests {
receipt,
LocalizedReceipt {
from: tx1.sender().into(),
to: match tx1.action {
to: match tx1.tx().action {
Action::Create => None,
Action::Call(ref address) => Some(address.clone().into()),
},
transaction_hash: tx1.hash(),
transaction_index: 1,
transaction_type: tx1.tx_type(),
block_hash: block_hash,
block_number: block_number,
cumulative_gas_used: gas_used,

View File

@ -151,7 +151,7 @@ impl Default for ClientConfig {
history_mem: 32 * mb,
check_seal: true,
transaction_verification_queue_size: 8192,
max_round_blocks_to_import: 12,
max_round_blocks_to_import: 1,
snapshot: Default::default(),
}
}

View File

@ -108,6 +108,7 @@ impl<'a> EvmTestClient<'a> {
Some(ethereum::new_byzantium_to_constantinoplefixat5_test())
}
ForkSpec::Berlin => Some(ethereum::new_berlin_test()),
ForkSpec::Yolo3 => Some(ethereum::new_yolo3_test()),
ForkSpec::FrontierToHomesteadAt5
| ForkSpec::HomesteadToDaoAt5
| ForkSpec::HomesteadToEIP150At5
@ -280,7 +281,7 @@ impl<'a> EvmTestClient<'a> {
tracer: T,
vm_tracer: V,
) -> std::result::Result<TransactSuccess<T::Output, V::Output>, TransactErr> {
let initial_gas = transaction.gas;
let initial_gas = transaction.tx().gas;
// Verify transaction
let is_ok = transaction.verify_basic(true, None);
if let Err(error) = is_ok {
@ -341,18 +342,18 @@ impl<'a> EvmTestClient<'a> {
Ok(result) => Ok(TransactSuccess {
state_root,
gas_left: initial_gas - result.receipt.gas_used,
outcome: result.receipt.outcome,
outcome: result.receipt.outcome.clone(),
output: result.output,
trace: result.trace,
vm_trace: result.vm_trace,
logs: result.receipt.logs,
contract_address: if let transaction::Action::Create = transaction.action {
logs: result.receipt.logs.clone(),
contract_address: if let transaction::Action::Create = transaction.tx().action {
Some(
executive::contract_address(
scheme,
&transaction.sender(),
&transaction.nonce,
&transaction.data,
&transaction.tx().nonce,
&transaction.tx().data,
)
.0,
)

View File

@ -36,7 +36,7 @@ use itertools::Itertools;
use kvdb::DBValue;
use kvdb_memorydb;
use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
use rlp::RlpStream;
use rustc_hex::FromHex;
use types::{
basic_account::BasicAccount,
@ -45,8 +45,11 @@ use types::{
header::Header,
log_entry::LocalizedLogEntry,
pruning_info::PruningInfo,
receipt::{LocalizedReceipt, Receipt, TransactionOutcome},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, Transaction},
receipt::{LegacyReceipt, LocalizedReceipt, TransactionOutcome, TypedReceipt},
transaction::{
self, Action, LocalizedTransaction, SignedTransaction, Transaction, TypedTransaction,
TypedTxId,
},
view,
views::BlockView,
BlockNumber,
@ -296,16 +299,16 @@ impl TestBlockChainClient {
for _ in 0..num_transactions {
// Update nonces value
let tx = Transaction {
let tx = TypedTransaction::Legacy(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);
signed_tx.rlp_append(&mut txs);
nonce += U256::one();
}
@ -369,14 +372,14 @@ impl TestBlockChainClient {
/// 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 {
let tx = TypedTransaction::Legacy(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();
@ -938,10 +941,13 @@ impl BlockChainClient for TestBlockChainClient {
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
// starts with 'f' ?
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
let receipt = BlockReceipts::new(vec![Receipt::new(
TransactionOutcome::StateRoot(H256::zero()),
U256::zero(),
vec![],
let receipt = BlockReceipts::new(vec![TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StateRoot(H256::zero()),
U256::zero(),
vec![],
),
)]);
return Some(receipt);
}
@ -1027,16 +1033,20 @@ impl BlockChainClient for TestBlockChainClient {
}
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
let transaction = Transaction {
let transaction = TypedTransaction::Legacy(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 sig = self
.spec
.engine
.sign(transaction.signature_hash(chain_id))
.unwrap();
let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap();
self.miner.import_own_transaction(self, signed.into())
}
@ -1051,7 +1061,7 @@ impl IoClient for TestBlockChainClient {
// import right here
let txs = transactions
.into_iter()
.filter_map(|bytes| Rlp::new(&bytes).as_val().ok())
.filter_map(|bytes| TypedTransaction::decode(&bytes).ok())
.collect();
self.miner.import_external_transactions(self, txs);
}

View File

@ -1307,6 +1307,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
Ok(())
}
// t_nb 8.1.5
fn on_new_block(
&self,
block: &mut ExecutedBlock,
@ -1531,7 +1532,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
Ok(())
}
// Check the validators.
// t_nb 6.4 Check the validators.
fn verify_block_external(&self, header: &Header) -> Result<(), Error> {
let (validators, set_number) = self.epoch_set(header)?;
@ -1764,7 +1765,7 @@ mod tests {
use test_helpers::{generate_dummy_client_with_spec, get_temp_state_db, TestNotify};
use types::{
header::Header,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
};
fn aura<F>(f: F) -> Arc<AuthorityRound>
@ -2286,14 +2287,14 @@ mod tests {
)
.unwrap();
b2.push_transaction(
Transaction {
TypedTransaction::Legacy(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,
)

View File

@ -488,7 +488,7 @@ pub trait Engine<M: Machine>: Sync + Send {
header_timestamp > parent_timestamp
}
/// Gather all ancestry actions. Called at the last stage when a block is committed. The Engine must guarantee that
// t_nb 9.1 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,
@ -507,7 +507,7 @@ pub trait Engine<M: Machine>: Sync + Send {
}
}
/// Check whether a given block is the best block based on the default total difficulty rule.
/// t_nb 9.3 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
@ -562,7 +562,7 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
self.machine().create_address_scheme(number)
}
/// Verify a particular transaction is valid.
// t_nb 5.3.1 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
@ -605,8 +605,10 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
fn decode_transaction(
&self,
transaction: &[u8],
best_block_number: BlockNumber,
) -> Result<UnverifiedTransaction, transaction::Error> {
self.machine().decode_transaction(transaction)
let schedule = self.schedule(best_block_number);
self.machine().decode_transaction(transaction, &schedule)
}
}

View File

@ -25,7 +25,7 @@ use kvdb::DBValue;
use memory_cache::MemoryLruCache;
use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
use types::{header::Header, ids::BlockId, log_entry::LogEntry, receipt::Receipt};
use types::{header::Header, ids::BlockId, log_entry::LogEntry, receipt::TypedReceipt};
use unexpected::Mismatch;
use super::{simple_list::SimpleList, SystemCall, ValidatorSet};
@ -91,7 +91,7 @@ fn check_first_proof(
old_header: Header,
state_items: &[DBValue],
) -> Result<Vec<Address>, String> {
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
// TODO: match client contract_call_tx more cleanly without duplication.
const PROVIDED_GAS: u64 = 50_000_000;
@ -116,14 +116,14 @@ fn check_first_proof(
let (data, decoder) = validator_set::functions::get_validators::call();
let from = Address::default();
let tx = Transaction {
let tx = TypedTransaction::Legacy(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(
@ -161,14 +161,15 @@ fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec<DBValue>), ::error::Erro
// 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 {
fn encode_proof(header: &Header, receipts: &[TypedReceipt]) -> Bytes {
let mut stream = RlpStream::new_list(2);
stream.append(header).append_list(receipts);
stream.append(header);
TypedReceipt::rlp_append_list(&mut stream, receipts);
stream.drain()
}
fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<Receipt>), ::error::Error> {
Ok((rlp.val_at(0)?, rlp.list_at(1)?))
fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<TypedReceipt>), ::error::Error> {
Ok((rlp.val_at(0)?, TypedReceipt::decode_rlp_list(&rlp.at(1)?)?))
}
// given a provider and caller, generate proof. this will just be a state proof
@ -265,7 +266,7 @@ impl ValidatorSafeContract {
&self,
bloom: Bloom,
header: &Header,
receipts: &[Receipt],
receipts: &[TypedReceipt],
) -> Option<SimpleList> {
let check_log = |log: &LogEntry| {
log.address == self.contract_address
@ -406,7 +407,7 @@ impl ValidatorSet for ValidatorSafeContract {
// ensure receipts match header.
// TODO: optimize? these were just decoded.
let found_root = ::triehash::ordered_trie_root(receipts.iter().map(::rlp::encode));
let found_root = ::triehash::ordered_trie_root(receipts.iter().map(|r| r.encode()));
if found_root != *old_header.receipts_root() {
return Err(::error::BlockError::InvalidReceiptsRoot(Mismatch {
expected: *old_header.receipts_root(),
@ -491,7 +492,7 @@ mod tests {
use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data};
use types::{
ids::BlockId,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
};
use verification::queue::kind::blocks::Unverified;
@ -537,7 +538,7 @@ mod tests {
client.miner().set_author(miner::Author::Sealer(signer));
// Remove "1" validator.
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 500_000.into(),
@ -546,7 +547,7 @@ mod tests {
data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1"
.from_hex()
.unwrap(),
}
})
.sign(&s0, Some(chain_id));
client
.miner()
@ -555,7 +556,7 @@ mod tests {
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1);
// Add "1" validator back in.
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: 1.into(),
gas_price: 0.into(),
gas: 500_000.into(),
@ -564,7 +565,7 @@ mod tests {
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1"
.from_hex()
.unwrap(),
}
})
.sign(&s0, Some(chain_id));
client
.miner()
@ -582,14 +583,14 @@ mod tests {
// 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 {
let tx = TypedTransaction::Legacy(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()

View File

@ -252,6 +252,11 @@ pub fn new_berlin_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/berlin_test.json"))
}
/// Create a new YOLO spec
pub fn new_yolo3_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/yolo3_test.json"))
}
/// Create a new Musicoin-MCIP3-era spec.
pub fn new_mcip3_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/mcip3_test.json"))

View File

@ -16,7 +16,6 @@
//! Transaction Execution environment.
use bytes::{Bytes, BytesRef};
use crossbeam_utils::thread;
use ethereum_types::{Address, H256, U256, U512};
use evm::{CallType, FinalizationResult, Finalize};
use executed::ExecutionError;
@ -29,28 +28,12 @@ use state::{Backend as StateBackend, CleanupMode, State, Substate};
use std::{cmp, sync::Arc};
use trace::{self, Tracer, VMTracer};
use transaction_ext::Transaction;
use types::transaction::{Action, SignedTransaction};
use types::transaction::{Action, SignedTransaction, TypedTransaction};
use vm::{
self, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo, ResumeCall,
ResumeCreate, ReturnData, Schedule, TrapError,
self, AccessList, 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)
const STACK_SIZE_PER_DEPTH: usize = 128 * 1024;
#[cfg(not(debug_assertions))]
/// Roughly estimate what stack size each level of evm depth will use.
const STACK_SIZE_PER_DEPTH: usize = 24 * 1024;
#[cfg(debug_assertions)]
/// Entry stack overhead prior to execution. (Debug build)
const STACK_SIZE_ENTRY_OVERHEAD: usize = 100 * 1024;
#[cfg(not(debug_assertions))]
/// 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<Address> = Some(ethereum_types::H160([
@ -252,6 +235,18 @@ pub struct CallCreateExecutive<'a> {
}
impl<'a> CallCreateExecutive<'a> {
/// Create new state with access list.
pub fn new_substate(params: &ActionParams, schedule: &'a Schedule) -> Substate {
if schedule.eip2929 {
let mut substate = Substate::from_access_list(&params.access_list);
substate.access_list.insert_address(params.address);
substate.access_list.insert_address(params.sender);
substate
} else {
Substate::default()
}
}
/// Create a new call executive using raw data.
pub fn new_call_raw(
params: ActionParams,
@ -287,7 +282,8 @@ impl<'a> CallCreateExecutive<'a> {
CallCreateExecutiveKind::CallBuiltin(params)
} else {
if params.code.is_some() {
CallCreateExecutiveKind::ExecCall(params, Substate::new())
let substate = Self::new_substate(&params, schedule);
CallCreateExecutiveKind::ExecCall(params, substate)
} else {
CallCreateExecutiveKind::Transfer(params)
}
@ -327,7 +323,8 @@ impl<'a> CallCreateExecutive<'a> {
let gas = params.gas;
let kind = CallCreateExecutiveKind::ExecCreate(params, Substate::new());
let substate = Self::new_substate(&params, schedule);
let kind = CallCreateExecutiveKind::ExecCreate(params, substate);
Self {
info,
@ -457,6 +454,7 @@ impl<'a> CallCreateExecutive<'a> {
}
}
state.revert_to_checkpoint();
un_substate.access_list.rollback();
}
Ok(_) | Err(vm::Error::Internal(_)) => {
state.discard_checkpoint();
@ -1111,7 +1109,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
{
let sender = t.sender();
let balance = self.state.balance(&sender)?;
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price));
let needed_balance = t
.tx()
.value
.saturating_add(t.tx().gas.saturating_mul(t.tx().gas_price));
if balance < needed_balance {
// give the sender a sufficient balance
self.state
@ -1134,16 +1135,51 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
T: Tracer,
V: VMTracer,
{
let schedule = self.schedule;
// check if particualar transaction type is enabled at this block number in schedule
match t.as_unsigned() {
TypedTransaction::AccessList(_) => {
if !schedule.eip2930 {
return Err(ExecutionError::TransactionMalformed(
"OptionalAccessList EIP-2930 or EIP-2929 not enabled".into(),
));
}
}
TypedTransaction::Legacy(_) => (), //legacy transactions are allways valid
};
let sender = t.sender();
let nonce = self.state.nonce(&sender)?;
let schedule = self.schedule;
let base_gas_required = U256::from(t.gas_required(&schedule));
let mut base_gas_required = U256::from(t.tx().gas_required(&schedule));
if t.gas < base_gas_required {
let mut access_list = AccessList::new(schedule.eip2929);
if schedule.eip2929 {
for (address, _) in self.machine.builtins() {
access_list.insert_address(*address);
}
if schedule.eip2930 {
// optional access list
if let TypedTransaction::AccessList(al_tx) = t.as_unsigned() {
for item in al_tx.access_list.iter() {
access_list.insert_address(item.0);
base_gas_required += vm::schedule::EIP2930_ACCESS_LIST_ADDRESS_COST.into();
for key in item.1.iter() {
access_list.insert_storage_key(item.0, *key);
base_gas_required +=
vm::schedule::EIP2930_ACCESS_LIST_STORAGE_KEY_COST.into();
}
}
}
}
}
if t.tx().gas < base_gas_required {
return Err(ExecutionError::NotEnoughBaseGas {
required: base_gas_required,
got: t.gas,
got: t.tx().gas,
});
}
@ -1155,29 +1191,29 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
return Err(ExecutionError::SenderMustExist);
}
let init_gas = t.gas - base_gas_required;
let init_gas = t.tx().gas - base_gas_required;
// validate transaction nonce
if check_nonce && t.nonce != nonce {
if check_nonce && t.tx().nonce != nonce {
return Err(ExecutionError::InvalidNonce {
expected: nonce,
got: t.nonce,
got: t.tx().nonce,
});
}
// validate if transaction fits into given block
if self.info.gas_used + t.gas > self.info.gas_limit {
if self.info.gas_used + t.tx().gas > self.info.gas_limit {
return Err(ExecutionError::BlockGasLimitReached {
gas_limit: self.info.gas_limit,
gas_used: self.info.gas_used,
gas: t.gas,
gas: t.tx().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;
let gas_cost = t.tx().gas.full_mul(t.tx().gas_price);
let total_cost = U512::from(t.tx().value) + gas_cost;
// avoid unaffordable transactions
let balance512 = U512::from(balance);
@ -1188,7 +1224,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
});
}
let mut substate = Substate::new();
let mut substate = Substate::from_access_list(&access_list);
// NOTE: there can be no invalid transactions from this point.
if !schedule.keep_unsigned_nonce || !t.is_unsigned() {
@ -1200,13 +1236,13 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
&mut substate.to_cleanup_mode(&schedule),
)?;
let (result, output) = match t.action {
let (result, output) = match t.tx().action {
Action::Create => {
let (new_address, code_hash) = contract_address(
self.machine.create_address_scheme(self.info.number),
&sender,
&nonce,
&t.data,
&t.tx().data,
);
let params = ActionParams {
code_address: new_address.clone(),
@ -1215,12 +1251,13 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
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())),
gas_price: t.tx().gas_price,
value: ActionValue::Transfer(t.tx().value),
code: Some(Arc::new(t.tx().data.clone())),
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
access_list: access_list,
};
let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer);
let out = match &res {
@ -1236,13 +1273,14 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
sender: sender.clone(),
origin: sender.clone(),
gas: init_gas,
gas_price: t.gas_price,
value: ActionValue::Transfer(t.value),
gas_price: t.tx().gas_price,
value: ActionValue::Transfer(t.tx().value),
code: self.state.code(address)?,
code_hash: self.state.code_hash(address)?,
data: Some(t.data.clone()),
data: Some(t.tx().data.clone()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
access_list: access_list,
};
let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer);
let out = match &res {
@ -1325,48 +1363,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
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<T, V>(
&mut self,
params: ActionParams,
substate: &mut Substate,
stack_depth: usize,
tracer: &mut T,
vm_tracer: &mut V,
) -> vm::Result<FinalizationResult>
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<T, V>(
&mut self,
@ -1437,54 +1433,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
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<T, V>(
&mut self,
params: ActionParams,
substate: &mut Substate,
stack_depth: usize,
tracer: &mut T,
vm_tracer: &mut V,
) -> vm::Result<FinalizationResult>
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<T, V>(
&mut self,
@ -1528,12 +1476,12 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
Ok(FinalizationResult { gas_left, .. }) => gas_left,
_ => 0.into(),
};
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1);
let refunded = cmp::min(refunds_bound, (t.tx().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);
let gas_used = t.tx().gas.saturating_sub(gas_left);
let (refund_value, overflow_1) = gas_left.overflowing_mul(t.tx().gas_price);
let (fees_value, overflow_2) = gas_used.overflowing_mul(t.tx().gas_price);
if overflow_1 || overflow_2 {
return Err(ExecutionError::TransactionMalformed(
"U256 Overflow".to_string(),
@ -1541,7 +1489,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
}
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);
t.tx().gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
let sender = t.sender();
trace!(
@ -1570,7 +1518,11 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
// perform garbage-collection
let min_balance = if schedule.kill_dust != CleanDustMode::Off {
Some(U256::from(schedule.tx_gas).overflowing_mul(t.gas_price).0)
Some(
U256::from(schedule.tx_gas)
.overflowing_mul(t.tx().gas_price)
.0,
)
} else {
None
};
@ -1585,10 +1537,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)),
Err(exception) => Ok(Executed {
exception: Some(exception),
gas: t.gas,
gas_used: t.gas,
gas: t.tx().gas,
gas_used: t.tx().gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
cumulative_gas_used: self.info.gas_used + t.tx().gas,
logs: vec![],
contracts_created: vec![],
output: output,
@ -1602,7 +1554,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
} else {
Some(vm::Error::Reverted)
},
gas: t.gas,
gas: t.tx().gas,
gas_used: gas_used,
refunded: refunded,
cumulative_gas_used: self.info.gas_used + gas_used,
@ -1634,7 +1586,7 @@ mod tests {
trace, ExecutiveTracer, ExecutiveVMTracer, FlatTrace, MemoryDiff, NoopTracer, NoopVMTracer,
StorageDiff, Tracer, VMExecutedOperation, VMOperation, VMTrace, VMTracer,
};
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
use vm::{ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo};
fn make_frontier_machine(max_depth: usize) -> EthereumMachine {
@ -2528,14 +2480,14 @@ mod tests {
evm_test_ignore! {test_transact_simple: test_transact_simple_int}
fn test_transact_simple(factory: Factory) {
let keypair = Random.generate().unwrap();
let t = Transaction {
let t = TypedTransaction::Legacy(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(
@ -2579,14 +2531,14 @@ mod tests {
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 {
let t = TypedTransaction::Legacy(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();
@ -2618,14 +2570,14 @@ mod tests {
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 {
let t = TypedTransaction::Legacy(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();
@ -2663,14 +2615,14 @@ mod tests {
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 {
let t = TypedTransaction::Legacy(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();
@ -2919,7 +2871,7 @@ mod tests {
let mut info = EnvInfo::default();
// 100 > 10
// 200 (wasmDisableTransition) > 100 > 10 (wasmActivationTransition)
info.number = 100;
// Network with wasm activated at block 10
@ -2947,9 +2899,35 @@ mod tests {
// Transaction successfully returned sender
assert_eq!(output[..], sender[..]);
// 1 < 10
// 1 < 10 (wasmActivationTransition)
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.clone(),
&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][..]);
// 200 == wasmDisableTransition
info.number = 200;
let mut output = [0u8; 20];
let FinalizationResult {
gas_left: result,
@ -2970,7 +2948,7 @@ mod tests {
.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
// Since transaction errored due to wasm was deactivated, result is just empty
assert_eq!(output[..], [0u8; 20][..]);
}
}

View File

@ -24,8 +24,8 @@ use std::{cmp, sync::Arc};
use trace::{Tracer, VMTracer};
use types::transaction::UNSIGNED_SENDER;
use vm::{
self, ActionParams, ActionValue, CallType, ContractCreateResult, CreateContractAddress,
EnvInfo, Ext, MessageCallResult, ReturnData, Schedule, TrapKind,
self, AccessList, ActionParams, ActionValue, CallType, ContractCreateResult,
CreateContractAddress, EnvInfo, Ext, MessageCallResult, ReturnData, Schedule, TrapKind,
};
/// Policy for handling output data on `RETURN` opcode.
@ -200,10 +200,11 @@ where
data: Some(H256::from(number).to_vec()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
access_list: AccessList::default(),
};
let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule);
let r = ex.call_with_crossbeam(
let r = ex.call_with_stack_depth(
params,
self.substate,
self.stack_depth + 1,
@ -288,6 +289,7 @@ where
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
access_list: self.substate.access_list.clone(),
};
if !self.static_flag {
@ -312,7 +314,7 @@ where
self.depth,
self.static_flag,
);
let out = ex.create_with_crossbeam(
let out = ex.create_with_stack_depth(
params,
self.substate,
self.stack_depth + 1,
@ -322,6 +324,15 @@ where
Ok(into_contract_create_result(out, &address, self.substate))
}
fn calc_address(&self, code: &[u8], address_scheme: CreateContractAddress) -> Option<Address> {
match self.state.nonce(&self.origin_info.address) {
Ok(nonce) => {
Some(contract_address(address_scheme, &self.origin_info.address, &nonce, &code).0)
}
Err(_) => None,
}
}
fn call(
&mut self,
gas: &U256,
@ -358,6 +369,7 @@ where
data: Some(data.to_vec()),
call_type: call_type,
params_type: vm::ParamsType::Separate,
access_list: self.substate.access_list.clone(),
};
if let Some(value) = value {
@ -376,7 +388,7 @@ where
self.depth,
self.static_flag,
);
let out = ex.call_with_crossbeam(
let out = ex.call_with_stack_depth(
params,
self.substate,
self.stack_depth + 1,
@ -518,6 +530,26 @@ where
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
self.vm_tracer.trace_executed(gas_used, stack_push, mem)
}
fn al_is_enabled(&self) -> bool {
self.substate.access_list.is_enabled()
}
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool {
self.substate.access_list.contains_storage_key(address, key)
}
fn al_insert_storage_key(&mut self, address: Address, key: H256) {
self.substate.access_list.insert_storage_key(address, key)
}
fn al_contains_address(&self, address: &Address) -> bool {
self.substate.access_list.contains_address(address)
}
fn al_insert_address(&mut self, address: Address) {
self.substate.access_list.insert_address(address)
}
}
#[cfg(test)]

View File

@ -167,6 +167,10 @@ where
Ok(ContractCreateResult::Created(contract_address, *gas))
}
fn calc_address(&self, code: &[u8], address: CreateContractAddress) -> Option<Address> {
Some(contract_address(address, &self.sender, &self.nonce, &code).0)
}
fn call(
&mut self,
gas: &U256,
@ -238,8 +242,29 @@ where
fn sub_sstore_refund(&mut self, value: usize) {
self.ext.sub_sstore_refund(value)
}
fn al_is_enabled(&self) -> bool {
self.ext.al_is_enabled()
}
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool {
self.ext.al_contains_storage_key(address, key)
}
fn al_insert_storage_key(&mut self, address: Address, key: H256) {
self.ext.al_insert_storage_key(address, key)
}
fn al_contains_address(&self, address: &Address) -> bool {
self.ext.al_contains_address(address)
}
fn al_insert_address(&mut self, address: Address) {
self.ext.al_insert_address(address)
}
}
/// run an json executive test
pub fn json_executive_test<H: FnMut(&str, HookType)>(
path: &Path,
json_data: &[u8],

View File

@ -19,12 +19,14 @@
mod chain;
mod difficulty;
mod executive;
pub mod runner;
mod state;
mod test_common;
mod transaction;
mod trie;
/// executor of ethereum/json tests
pub mod runner;
pub use self::{
executive::json_executive_test,
test_common::{debug_include_test, find_json_files_recursive, HookType},

View File

@ -59,6 +59,7 @@ impl std::ops::AddAssign for TestResult {
}
}
/// An executor of ethereum/json tests
pub struct TestRunner(EthereumTestSuite);
impl TestRunner {

View File

@ -68,7 +68,6 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
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 => {
@ -81,11 +80,10 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
for (i, state) in states.into_iter().enumerate() {
let info = format!(
" - state: {} | {:?} ({}/{}) ...",
name,
"TestState/{}/{:?}/{}/trie",
path.to_string_lossy(),
spec_name,
i + 1,
total
i
);
if skip_test(&state_test, &name, &spec.name, i + 1) {
println!("{}: SKIPPED", info);
@ -112,7 +110,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
}
Ok(Ok(TransactSuccess { state_root, .. })) if state_root != post_root => {
println!(
"{} !!! State mismatch (got: {}, expect: {}",
"{}: post state root mismatch: got {:?}, want {:?}",
info, state_root, post_root
);
flushln!("{} fail", info);
@ -124,7 +122,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
..
})) if state_root != post_root => {
println!(
"{} !!! State mismatch (got: {}, expect: {}",
"{}: post state root mismatch: got {:?}, want {:?}",
info, state_root, post_root
);
println!("{} !!! Execution error: {:?}", info, error);

View File

@ -27,6 +27,7 @@ pub enum HookType {
OnStop,
}
/// find all json files recursively from a path
pub fn find_json_files_recursive(path: &PathBuf) -> Vec<PathBuf> {
WalkDir::new(path)
.into_iter()
@ -36,6 +37,7 @@ pub fn find_json_files_recursive(path: &PathBuf) -> Vec<PathBuf> {
.collect::<Vec<PathBuf>>()
}
/// 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| {

View File

@ -17,10 +17,12 @@
use super::test_common::*;
use client::EvmTestClient;
use ethjson;
use rlp::Rlp;
use std::path::Path;
use transaction_ext::Transaction;
use types::{header::Header, transaction::UnverifiedTransaction};
use types::{
header::Header,
transaction::{TypedTransaction, UnverifiedTransaction},
};
pub fn json_transaction_test<H: FnMut(&str, HookType)>(
path: &Path,
@ -65,8 +67,7 @@ pub fn json_transaction_test<H: FnMut(&str, HookType)>(
};
let rlp: Vec<u8> = test.rlp.clone().into();
let res = Rlp::new(&rlp)
.as_val()
let res = TypedTransaction::decode(&rlp)
.map_err(::error::Error::from)
.and_then(|t: UnverifiedTransaction| {
let mut header: Header = Default::default();
@ -74,12 +75,13 @@ pub fn json_transaction_test<H: FnMut(&str, HookType)>(
header.set_number(BLOCK_NUMBER);
let minimal = t
.tx()
.gas_required(&spec.engine.schedule(header.number()))
.into();
if t.gas < minimal {
if t.tx().gas < minimal {
return Err(::types::transaction::Error::InsufficientGas {
minimal,
got: t.gas,
got: t.tx().gas,
}
.into());
}

View File

@ -23,16 +23,17 @@ use std::{
};
use ethereum_types::{Address, H256, U256};
use rlp::Rlp;
use types::{
header::Header,
transaction::{
self, SignedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS, UNSIGNED_SENDER,
self, SignedTransaction, TypedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS,
UNSIGNED_SENDER,
},
BlockNumber,
};
use vm::{
ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType, Schedule,
AccessList, ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType,
Schedule,
};
use block::ExecutedBlock;
@ -201,6 +202,7 @@ impl EthereumMachine {
data,
call_type: call_type.unwrap_or(CallType::Call),
params_type: ParamsType::Separate,
access_list: AccessList::default(),
};
let schedule = self.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, self, &schedule);
@ -236,7 +238,7 @@ impl EthereumMachine {
Ok(())
}
/// Logic to perform on a new block: updating last hashes and the DAO
// t_nb 8.1.3 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)?;
@ -453,17 +455,27 @@ impl EthereumMachine {
pub fn decode_transaction(
&self,
transaction: &[u8],
schedule: &Schedule,
) -> Result<UnverifiedTransaction, transaction::Error> {
let rlp = Rlp::new(&transaction);
if rlp.as_raw().len() > self.params().max_transaction_size {
if transaction.len() > self.params().max_transaction_size {
debug!(
"Rejected oversized transaction of {} bytes",
rlp.as_raw().len()
transaction.len()
);
return Err(transaction::Error::TooBig);
}
rlp.as_val()
.map_err(|e| transaction::Error::InvalidRlp(e.to_string()))
let tx = TypedTransaction::decode(transaction)
.map_err(|e| transaction::Error::InvalidRlp(e.to_string()))?;
match tx.tx_type() {
transaction::TypedTxId::AccessList if schedule.eip2930 => {
return Err(transaction::Error::TransactionTypeNotEnabled)
}
_ => (),
};
Ok(tx)
}
}
@ -474,7 +486,7 @@ 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]>,
pub receipts: Option<&'a [::types::receipt::TypedReceipt]>,
}
/// Type alias for a function we can make calls through synchronously.
@ -548,7 +560,7 @@ mod tests {
fn should_disallow_unsigned_transactions() {
let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080";
let transaction: UnverifiedTransaction =
::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap();
TypedTransaction::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap();
let spec = ::ethereum::new_ropsten_test();
let ethparams = get_default_ethash_extensions();

View File

@ -1195,15 +1195,16 @@ impl miner::MinerService for Miner {
let receipt = &receipts[index];
RichReceipt {
from: tx.sender(),
to: match tx.action {
to: match tx.tx().action {
Action::Create => None,
Action::Call(ref address) => Some(*address),
},
transaction_hash: tx.hash(),
transaction_type: tx.tx_type(),
transaction_index: index,
cumulative_gas_used: receipt.gas_used,
gas_used: receipt.gas_used - prev_gas,
contract_address: match tx.action {
contract_address: match tx.tx().action {
Action::Call(_) => None,
Action::Create => {
let sender = tx.sender();
@ -1212,8 +1213,8 @@ impl miner::MinerService for Miner {
self.engine
.create_address_scheme(pending.header.number()),
&sender,
&tx.nonce,
&tx.data,
&tx.tx().nonce,
&tx.tx().data,
)
.0,
)
@ -1230,7 +1231,7 @@ impl miner::MinerService for Miner {
)
}
/// Update sealing if required.
// t_nb 10.4 Update sealing if required.
/// Prepare the block and work if the Engine does not seal internally.
fn update_sealing<C>(&self, chain: &C, force: ForceUpdateSealing)
where
@ -1339,6 +1340,7 @@ impl miner::MinerService for Miner {
})
}
// t_nb 10 notify miner about new include blocks
fn chain_new_blocks<C>(
&self,
chain: &C,
@ -1363,11 +1365,11 @@ impl miner::MinerService for Miner {
self.nonce_cache.clear();
}
// First update gas limit in transaction queue and minimal gas price.
// t_nb 10.1 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.
// t_nb 10.2 Then import all transactions from retracted blocks (retracted means from side chain).
let client = self.pool_client(chain);
{
retracted
@ -1378,7 +1380,8 @@ impl miner::MinerService for Miner {
let txs = block.transactions()
.into_iter()
.map(pool::verifier::Transaction::Retracted)
.collect();
.collect();
// t_nb 10.2
let _ = self.transaction_queue.import(
client.clone(),
txs,
@ -1387,12 +1390,13 @@ impl miner::MinerService for Miner {
}
if has_new_best_block || (imported.len() > 0 && self.options.reseal_on_uncle) {
// Reset `next_allowed_reseal` in case a block is imported.
// t_nb 10.3 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 {
// t_nb 10.4 if it is internal import update sealing
// --------------------------------------------------------------------------
// | NOTE Code below requires sealing locks. |
// | Make sure to release the locks before calling that method. |
@ -1402,7 +1406,7 @@ impl miner::MinerService for Miner {
}
if has_new_best_block {
// Make sure to cull transactions after we update sealing.
// t_nb 10.5 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
@ -1424,7 +1428,9 @@ impl miner::MinerService for Miner {
&*accounts,
service_transaction_checker.as_ref(),
);
// t_nb 10.5 do culling
queue.cull(client);
// reseal is only used by InstaSeal engine
if engine.should_reseal_on_update() {
// force update_sealing here to skip `reseal_required` checks
chain.update_sealing(ForceUpdateSealing::Yes);
@ -1435,13 +1441,16 @@ impl miner::MinerService for Miner {
warn!(target: "miner", "Error queueing cull: {:?}", e);
}
} else {
// t_nb 10.5 do culling
self.transaction_queue.cull(client);
// reseal is only used by InstaSeal engine
if self.engine.should_reseal_on_update() {
// force update_sealing here to skip `reseal_required` checks
self.update_sealing(chain, ForceUpdateSealing::Yes);
}
}
}
// t_nb 10.6 For service transaction checker update addresses to latest block
if let Some(ref service_transaction_checker) = self.service_transaction_checker {
match service_transaction_checker.refresh_cache(chain) {
Ok(true) => {
@ -1501,7 +1510,7 @@ mod tests {
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;
use types::transaction::{Transaction, TypedTransaction};
#[test]
fn should_prepare_block_to_seal() {
@ -1575,14 +1584,14 @@ mod tests {
fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction {
let keypair = Random.generate().unwrap();
Transaction {
TypedTransaction::Legacy(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))
}

View File

@ -195,7 +195,8 @@ where
&self,
transaction: &[u8],
) -> Result<UnverifiedTransaction, transaction::Error> {
self.engine.decode_transaction(transaction)
let number = self.chain.best_block_header().number();
self.engine.decode_transaction(transaction, number)
}
}

View File

@ -21,7 +21,7 @@ use ethereum_types::H256;
use hash::keccak;
use rlp::{DecoderError, Rlp, RlpStream};
use triehash::ordered_trie_root;
use types::{block::Block, header::Header, views::BlockView};
use types::{block::Block, header::Header, transaction::TypedTransaction, views::BlockView};
const HEADER_FIELDS: usize = 8;
const BLOCK_FIELDS: usize = 2;
@ -62,9 +62,9 @@ impl AbridgedBlock {
.append(&header.extra_data());
// write block values.
stream
.append_list(&block_view.transactions())
.append_list(&block_view.uncles());
TypedTransaction::rlp_append_list(&mut stream, &block_view.transactions());
stream.append_list(&block_view.uncles());
// write seal fields.
for field in seal_fields {
@ -97,10 +97,17 @@ impl AbridgedBlock {
header.set_timestamp(rlp.val_at(6)?);
header.set_extra_data(rlp.val_at(7)?);
let transactions = rlp.list_at(8)?;
let transactions = TypedTransaction::decode_rlp_list(&rlp.at(8)?)?;
let uncles: Vec<Header> = rlp.list_at(9)?;
header.set_transactions_root(ordered_trie_root(rlp.at(8)?.iter().map(|r| r.as_raw())));
header.set_transactions_root(ordered_trie_root(rlp.at(8)?.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// We already checked if list is valid with decode_rlp_list above
r.data().expect("To raw rlp list to be valid")
}
})));
header.set_receipts_root(receipts_root);
let mut uncles_rlp = RlpStream::new();
@ -131,7 +138,7 @@ mod tests {
use ethereum_types::{Address, H256, U256};
use types::{
block::Block,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -165,24 +172,24 @@ mod tests {
fn with_transactions() {
let mut b = Block::default();
let t1 = Transaction {
let t1 = TypedTransaction::Legacy(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 {
let t2 = TypedTransaction::Legacy(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());
@ -191,7 +198,7 @@ mod tests {
let receipts_root = b.header.receipts_root().clone();
b.header
.set_transactions_root(::triehash::ordered_trie_root(
b.transactions.iter().map(::rlp::encode),
b.transactions.iter().map(|tx| tx.encode()),
));
let encoded = encode_block(&b);

View File

@ -36,7 +36,9 @@ use ethereum_types::{H256, U256};
use itertools::{Itertools, Position};
use kvdb::KeyValueDB;
use rlp::{Rlp, RlpStream};
use types::{encoded, header::Header, ids::BlockId, receipt::Receipt};
use types::{
encoded, header::Header, ids::BlockId, receipt::TypedReceipt, transaction::TypedTransaction,
};
/// Snapshot creation and restoration for PoA chains.
/// Chunk format:
@ -114,9 +116,9 @@ impl SnapshotComponents for PoaSnapshot {
rlps.push({
let mut stream = RlpStream::new_list(5);
stream.append(&block.header);
TypedTransaction::rlp_append_list(&mut stream, &block.transactions);
stream
.append(&block.header)
.append_list(&block.transactions)
.append_list(&block.uncles)
.append(&receipts)
.append(&parent_td);
@ -349,11 +351,11 @@ impl Rebuilder for ChunkRebuilder {
let last_rlp = rlp.at(num_items - 1)?;
let block = Block {
header: last_rlp.val_at(0)?,
transactions: last_rlp.list_at(1)?,
transactions: TypedTransaction::decode_rlp_list(&last_rlp.at(1)?)?,
uncles: last_rlp.list_at(2)?,
};
let block_data = block.rlp_bytes();
let receipts: Vec<Receipt> = last_rlp.list_at(3)?;
let receipts = TypedReceipt::decode_rlp_list(&last_rlp.at(3)?)?;
{
let hash = block.header.hash();

View File

@ -291,8 +291,15 @@ impl Rebuilder for PowRebuilder {
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 receipts = ::types::receipt::TypedReceipt::decode_rlp_list(&pair.at(1)?)?;
let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// We have allready checked validity by decoding rlp list in line above
r.data().expect("Expect for raw receipts list to be valid.")
}
}));
let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?;
let block_bytes = encoded::Block::new(block.rlp_bytes());

View File

@ -17,7 +17,7 @@
//! Snapshot creation, restoration, and network service.
//!
//! Documentation of the format can be found at
//! https://openethereum.github.io/wiki/Warp-Sync-Snapshot-Format
//! https://openethereum.github.io/Warp-Sync-Snapshot-Format
use hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP};
use std::{

View File

@ -25,7 +25,7 @@ use snapshot::tests::helpers as snapshot_helpers;
use spec::Spec;
use tempdir::TempDir;
use test_helpers::generate_dummy_client_with_spec;
use types::transaction::{Action, SignedTransaction, Transaction};
use types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
use ethereum_types::Address;
use test_helpers;
@ -128,14 +128,14 @@ fn make_chain(
// and force sealing.
let make_useless_transactions = || {
let mut nonce = nonce.borrow_mut();
let transaction = Transaction {
let transaction = TypedTransaction::Legacy(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;
@ -171,14 +171,14 @@ fn make_chain(
let data =
test_validator_set::functions::set_validators::encode_input(new_set.clone());
let mut nonce = nonce.borrow_mut();
let transaction = Transaction {
let transaction = TypedTransaction::Legacy(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;

View File

@ -301,7 +301,7 @@ fn recover_aborted_recovery() {
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 tempdir = TempDir::new("oe_snapshot").unwrap();
let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let client_db = new_db();
let client2 = Client::new(

View File

@ -111,6 +111,7 @@ impl Watcher {
}
impl ChainNotify for Watcher {
// t_nb 11.1 check number of block and trigger snapshot creation if needed.
fn new_blocks(&self, new_blocks: NewBlocks) {
if self.oracle.is_major_importing() || new_blocks.has_more_blocks_to_import {
return;

View File

@ -32,7 +32,7 @@ use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
use rustc_hex::FromHex;
use types::{header::Header, BlockNumber};
use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use vm::{AccessList, ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use builtin::Builtin;
use engines::{
@ -135,6 +135,10 @@ pub struct CommonParams {
pub eip2028_transition: BlockNumber,
/// Number of first block where EIP-2315 rules begin.
pub eip2315_transition: BlockNumber,
/// Number of first block where EIP-2929 rules begin.
pub eip2929_transition: BlockNumber,
/// Number of first block where EIP-2930 rules begin.
pub eip2930_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.
@ -143,6 +147,8 @@ pub struct CommonParams {
pub remove_dust_contracts: bool,
/// Wasm activation blocknumber, if any disabled initially.
pub wasm_activation_transition: BlockNumber,
/// Wasm deactivation blocknumber, if enabled.
pub wasm_disable_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.
@ -207,6 +213,8 @@ impl CommonParams {
|| block_number >= self.eip1283_reenable_transition;
schedule.eip1706 = block_number >= self.eip1706_transition;
schedule.have_subs = block_number >= self.eip2315_transition;
schedule.eip2929 = block_number >= self.eip2929_transition;
schedule.eip2930 = block_number >= self.eip2930_transition;
if block_number >= self.eip1884_transition {
schedule.have_selfbalance = true;
@ -220,13 +228,33 @@ impl CommonParams {
if block_number >= self.eip210_transition {
schedule.blockhash_gas = 800;
}
if block_number >= self.eip2929_transition {
schedule.eip2929 = true;
schedule.eip1283 = true;
schedule.call_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.balance_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodecopy_base_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodehash_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodesize_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.cold_sload_cost = ::vm::schedule::EIP2929_COLD_SLOAD_COST;
schedule.cold_account_access_cost = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.warm_storage_read_cost = ::vm::schedule::EIP2929_WARM_STORAGE_READ_COST;
schedule.sload_gas = ::vm::schedule::EIP2929_WARM_STORAGE_READ_COST;
schedule.sstore_reset_gas = ::vm::schedule::EIP2929_SSTORE_RESET_GAS;
}
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 {
if block_number >= self.wasm_activation_transition
&& block_number < self.wasm_disable_transition
{
let mut wasm = ::vm::WasmCosts::default();
if block_number >= self.kip4_transition {
wasm.have_create2 = true;
@ -342,6 +370,12 @@ impl From<ethjson::spec::Params> for CommonParams {
eip2315_transition: p
.eip2315_transition
.map_or_else(BlockNumber::max_value, Into::into),
eip2929_transition: p
.eip2929_transition
.map_or_else(BlockNumber::max_value, Into::into),
eip2930_transition: p
.eip2930_transition
.map_or_else(BlockNumber::max_value, Into::into),
dust_protection_transition: p
.dust_protection_transition
.map_or_else(BlockNumber::max_value, Into::into),
@ -362,6 +396,9 @@ impl From<ethjson::spec::Params> for CommonParams {
wasm_activation_transition: p
.wasm_activation_transition
.map_or_else(BlockNumber::max_value, Into::into),
wasm_disable_transition: p
.wasm_disable_transition
.map_or_else(BlockNumber::max_value, Into::into),
kip4_transition: p
.kip4_transition
.map_or_else(BlockNumber::max_value, Into::into),
@ -628,6 +665,7 @@ impl Spec {
params.eip2315_transition,
params.dust_protection_transition,
params.wasm_activation_transition,
params.wasm_disable_transition,
params.kip4_transition,
params.kip6_transition,
params.max_code_size_transition,
@ -749,6 +787,7 @@ impl Spec {
data: None,
call_type: CallType::None,
params_type: ParamsType::Embedded,
access_list: AccessList::default(),
};
let mut substate = Substate::new();
@ -923,7 +962,7 @@ impl Spec {
/// initialize genesis epoch data, using in-memory database for
/// constructor.
pub fn genesis_epoch_data(&self) -> Result<Vec<u8>, String> {
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
let genesis = self.genesis_header();
@ -950,14 +989,14 @@ impl Spec {
};
let from = Address::default();
let tx = Transaction {
let tx = TypedTransaction::Legacy(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(

View File

@ -43,7 +43,7 @@ pub trait Backend: Send {
/// Treat the backend as a writeable hashdb.
fn as_hash_db_mut(&mut self) -> &mut dyn HashDB<KeccakHasher, DBValue>;
/// Add an account entry to the cache.
/// t_nb 9.4 Add an account entry to the cache.
fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool);
/// Add a global code cache entry. This doesn't need to worry about canonicality because

View File

@ -38,10 +38,11 @@ use state_db::StateDB;
use trace::{self, FlatTrace, VMTrace};
use types::{
basic_account::BasicAccount,
receipt::{Receipt, TransactionOutcome},
receipt::{LegacyReceipt, TransactionOutcome, TypedReceipt},
state_diff::StateDiff,
transaction::SignedTransaction,
};
use vm::EnvInfo;
use bytes::Bytes;
@ -63,7 +64,7 @@ pub use self::{account::Account, backend::Backend, substate::Substate};
/// Used to return information about an `State::apply` operation.
pub struct ApplyOutcome<T, V> {
/// The receipt for the applied transaction.
pub receipt: Receipt,
pub receipt: TypedReceipt,
/// The output of the applied transaction.
pub output: Bytes,
/// The trace for the applied transaction, empty if tracing was not produced.
@ -955,7 +956,10 @@ impl<B: Backend> State<B> {
};
let output = e.output;
let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs);
let receipt = TypedReceipt::new(
t.tx_type(),
LegacyReceipt::new(outcome, e.cumulative_gas_used, e.logs),
);
trace!(target: "state", "Transaction receipt: {:?}", receipt);
Ok(ApplyOutcome {
@ -996,7 +1000,7 @@ impl<B: Backend> State<B> {
Ok(())
}
/// Commits our cached account changes into the trie.
/// t_nb 8.5.2 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.
@ -1036,7 +1040,7 @@ impl<B: Backend> State<B> {
Ok(())
}
/// Propagate local cache into shared canonical state cache.
// t_nb 9.4 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());
@ -1602,7 +1606,7 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -1610,7 +1614,7 @@ mod tests {
value: 100.into(),
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555")
.unwrap(),
}
})
.sign(&secret(), None);
state
@ -1667,14 +1671,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -1706,14 +1710,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -1753,14 +1757,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -1797,14 +1801,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(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();
@ -1839,14 +1843,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -1887,14 +1891,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -1957,14 +1961,14 @@ mod tests {
info.number = 0x789b0;
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2029,14 +2033,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2073,14 +2077,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2145,14 +2149,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2210,14 +2214,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2260,14 +2264,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2328,14 +2332,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2421,14 +2425,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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
@ -2512,14 +2516,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(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

View File

@ -20,6 +20,7 @@ use ethereum_types::Address;
use evm::{CleanDustMode, Schedule};
use std::collections::HashSet;
use types::log_entry::LogEntry;
use vm::access_list::AccessList;
/// State changes which should be applied in finalize,
/// after transaction is fully executed.
@ -39,6 +40,9 @@ pub struct Substate {
/// Created contracts.
pub contracts_created: Vec<Address>,
/// List of accesses addresses and slots
pub access_list: AccessList,
}
impl Substate {
@ -46,6 +50,17 @@ impl Substate {
pub fn new() -> Self {
Substate::default()
}
/// Creates a new substate from an access list
pub fn from_access_list(access_list: &AccessList) -> Self {
Self {
suicides: HashSet::default(),
touched: HashSet::default(),
logs: Vec::default(),
sstore_clears_refund: 0,
contracts_created: Vec::default(),
access_list: access_list.clone(),
}
}
/// Merge secondary substate `s` into self, accruing each element correspondingly.
pub fn accrue(&mut self, s: Substate) {

View File

@ -147,6 +147,7 @@ impl StateDB {
Ok(records)
}
// t_nb 9.15
/// 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(
@ -158,7 +159,7 @@ impl StateDB {
self.db.mark_canonical(batch, end_era, canon_id)
}
/// Propagate local cache into the global cache and synchonize
// t_nb 9.10 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`

View File

@ -36,7 +36,7 @@ use tempdir::TempDir;
use types::{
encoded,
header::Header,
transaction::{Action, SignedTransaction, Transaction},
transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -104,7 +104,7 @@ pub fn create_test_block_with_data(
rlp.append(header);
rlp.begin_list(transactions.len());
for t in transactions {
rlp.append_raw(&rlp::encode(t), 1);
t.rlp_append(&mut rlp);
}
rlp.append_list(&uncles);
rlp.out()
@ -197,14 +197,14 @@ where
// first block we don't have any balance, so can't send any transactions.
for _ in 0..txs_per_block {
b.push_transaction(
Transaction {
TypedTransaction::Legacy(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,
)

View File

@ -45,7 +45,7 @@ use types::{
data_format::DataFormat,
filter::Filter,
ids::BlockId,
transaction::{Action, Condition, PendingTransaction, Transaction},
transaction::{Action, Condition, PendingTransaction, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -278,7 +278,7 @@ fn can_handle_long_fork() {
push_blocks_to_client(&client, 49, 1201, 800);
push_blocks_to_client(&client, 53, 1201, 600);
for _ in 0..400 {
for _ in 0..2300 {
client.import_verified_blocks();
}
assert_eq!(2000, client.chain_info().best_block_number);
@ -362,26 +362,26 @@ fn does_not_propagate_delayed_transactions() {
let key = KeyPair::from_secret(keccak("test").into()).unwrap();
let secret = key.secret();
let tx0 = PendingTransaction::new(
Transaction {
TypedTransaction::Legacy(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 {
TypedTransaction::Legacy(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,
);
@ -443,14 +443,14 @@ fn transaction_proof() {
client.import_sealed_block(b).unwrap(); // account change is in the journal overlay
}
let transaction = Transaction {
let transaction = TypedTransaction::Legacy(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

View File

@ -24,7 +24,7 @@ use std::sync::Arc;
use test_helpers::get_temp_state_with_factory;
use trace::{NoopTracer, NoopVMTracer};
use types::transaction::SYSTEM_ADDRESS;
use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use vm::{AccessList, ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use rustc_hex::FromHex;
@ -62,6 +62,7 @@ fn test_blockhash_eip210(factory: Factory) {
data: Some(H256::from(i - 1).to_vec()),
call_type: CallType::Call,
params_type: ParamsType::Separate,
access_list: AccessList::default(),
};
let schedule = machine.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule);
@ -85,6 +86,7 @@ fn test_blockhash_eip210(factory: Factory) {
data: None,
call_type: CallType::Call,
params_type: ParamsType::Separate,
access_list: AccessList::default(),
};
let schedule = machine.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule);

View File

@ -29,7 +29,7 @@ use test_helpers::{self, get_temp_state_db};
use trace::{trace::Action::Reward, LocalizedTrace, RewardType};
use types::{
header::Header,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -171,14 +171,14 @@ fn can_trace_block_and_uncle_reward() {
for _ in 0..1 {
block
.push_transaction(
Transaction {
TypedTransaction::Legacy(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,
)

View File

@ -83,7 +83,7 @@ impl TransactionFilter {
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 {
let (tx_type, to) = match transaction.tx().action {
Action::Create => (tx_permissions::CREATE, Address::new()),
Action::Call(address) => {
if client
@ -98,7 +98,7 @@ impl TransactionFilter {
};
let sender = transaction.sender();
let value = transaction.value;
let value = transaction.tx().value;
let key = (*parent_hash, sender);
if let Some(permissions) = permission_cache.get_mut(&key) {
@ -181,7 +181,7 @@ mod test {
use std::sync::Arc;
use tempdir::TempDir;
use test_helpers;
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
/// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
#[test]
@ -230,28 +230,30 @@ mod test {
.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 =
let mut basic_tx = TypedTransaction::Legacy(Transaction::default());
basic_tx.tx_mut().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 =
let create_tx = TypedTransaction::Legacy(Transaction::default());
let mut call_tx = TypedTransaction::Legacy(Transaction::default());
call_tx.tx_mut().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 =
let mut basic_tx_with_ether_and_to_key7 = TypedTransaction::Legacy(Transaction::default());
basic_tx_with_ether_and_to_key7.tx_mut().action =
Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb"));
basic_tx_with_ether_and_to_key7.tx_mut().value = U256::from(123123);
let mut call_tx_with_ether = TypedTransaction::Legacy(Transaction::default());
call_tx_with_ether.tx_mut().action =
Action::Call(Address::from("0000000000000000000000000000000000000005"));
call_tx_with_ether.tx_mut().value = U256::from(123123);
let mut basic_tx_to_key6 = TypedTransaction::Legacy(Transaction::default());
basic_tx_to_key6.tx_mut().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 =
let mut basic_tx_with_ether_and_to_key6 = TypedTransaction::Legacy(Transaction::default());
basic_tx_with_ether_and_to_key6.tx_mut().action =
Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141"));
basic_tx_with_ether_and_to_key6.value = U256::from(123123);
basic_tx_with_ether_and_to_key6.tx_mut().value = U256::from(123123);
let genesis = client.block_hash(BlockId::Latest).unwrap();
let block_number = 1;
@ -444,11 +446,13 @@ mod test {
.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 mut basic_tx = TypedTransaction::Legacy(Transaction::default());
basic_tx.tx_mut().action =
Action::Call(Address::from("000000000000000000000000000000000000032"));
let create_tx = TypedTransaction::Legacy(Transaction::default());
let mut call_tx = TypedTransaction::Legacy(Transaction::default());
call_tx.tx_mut().action =
Action::Call(Address::from("0000000000000000000000000000000000000005"));
let genesis = client.block_hash(BlockId::Latest).unwrap();
let block_number = 1;

View File

@ -80,7 +80,10 @@ pub mod blocks {
use engines::EthEngine;
use error::{BlockError, Error, ErrorKind};
use types::{header::Header, transaction::UnverifiedTransaction};
use types::{
header::Header,
transaction::{TypedTransaction, UnverifiedTransaction},
};
use verification::{verify_block_basic, verify_block_unordered, PreverifiedBlock};
use bytes::Bytes;
@ -95,6 +98,7 @@ pub mod blocks {
type Unverified = Unverified;
type Verified = PreverifiedBlock;
// t_nb 4.0 verify_block_basic
fn create(
input: Self::Input,
engine: &dyn EthEngine,
@ -113,6 +117,7 @@ pub mod blocks {
}
}
// t_nb 5.0 verify standalone block
fn verify(
un: Self::Unverified,
engine: &dyn EthEngine,
@ -149,7 +154,7 @@ pub mod blocks {
let (header, transactions, uncles) = {
let rlp = Rlp::new(&bytes);
let header = rlp.val_at(0)?;
let transactions = rlp.list_at(1)?;
let transactions = TypedTransaction::decode_rlp_list(&rlp.at(1)?)?;
let uncles = rlp.list_at(2)?;
(header, transactions, uncles)
};

View File

@ -364,7 +364,7 @@ impl<K: Kind> VerificationQueue<K> {
}
}
// do work.
// do work on this item.
let item = {
// acquire these locks before getting the item to verify.
let mut unverified = verification.unverified.lock();
@ -387,10 +387,12 @@ impl<K: Kind> VerificationQueue<K> {
};
let hash = item.hash();
// t_nb 5.0 verify standalone block (this verification is done in VerificationQueue thread pool)
let is_ready = match K::verify(item, &*engine, verification.check_seal) {
Ok(verified) => {
let mut verifying = verification.verifying.lock();
let mut idx = None;
// find item again and remove it from verified queue
for (i, e) in verifying.iter_mut().enumerate() {
if e.hash == hash {
idx = Some(i);
@ -515,17 +517,20 @@ impl<K: Kind> VerificationQueue<K> {
}
/// Add a block to the queue.
// t_nb 3.0 import block to verification queue
pub fn import(&self, input: K::Input) -> Result<H256, (Option<K::Input>, Error)> {
let hash = input.hash();
let raw_hash = input.raw_hash();
// t_nb 3.1 check if block is currently processing or marked as bad.
{
// t_nb 3.1.0 is currently processing
if self.processing.read().contains_key(&hash) {
bail!((
Some(input),
ErrorKind::Import(ImportErrorKind::AlreadyQueued).into()
));
}
// t_nb 3.1.1 is marked as bad
let mut bad = self.verification.bad.lock();
if bad.contains(&hash) || bad.contains(&raw_hash) {
bail!((
@ -533,7 +538,7 @@ impl<K: Kind> VerificationQueue<K> {
ErrorKind::Import(ImportErrorKind::KnownBad).into()
));
}
// t_nb 3.1.2 its parent is marked as bad
if bad.contains(&input.parent_hash()) {
bad.insert(hash);
bail!((
@ -665,11 +670,17 @@ impl<K: Kind> VerificationQueue<K> {
.verified
.fetch_sub(drained_size, AtomicOrdering::SeqCst);
result
}
/// release taken signal and call async ClientIoMessage::BlockVerified call to client so that it can continue verification.
/// difference between sync and async is whose thread pool is used.
pub fn resignal_verification(&self) {
let verified = self.verification.verified.lock();
self.ready_signal.reset();
if !verified.is_empty() {
self.ready_signal.set_async();
}
result
}
/// Returns true if there is nothing currently in the queue.

View File

@ -63,26 +63,36 @@ impl HeapSizeOf for PreverifiedBlock {
}
}
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
/// t_nb 4.0 Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(
block: &Unverified,
engine: &dyn EthEngine,
check_seal: bool,
) -> Result<(), Error> {
// t_nb 4.1 verify header params
verify_header_params(&block.header, engine, true, check_seal)?;
// t_nb 4.2 verify header time (addded in new OE version)
// t_nb 4.3 verify block integrity
verify_block_integrity(block)?;
if check_seal {
// t_nb 4.4 Check block seal. It calls engine to verify block basic
engine.verify_block_basic(&block.header)?;
}
// t_nb 4.5 for all uncled verify header and call engine to verify block basic
for uncle in &block.uncles {
// t_nb 4.5.1
verify_header_params(uncle, engine, false, check_seal)?;
if check_seal {
// t_nb 4.5.2
engine.verify_block_basic(uncle)?;
}
}
// t_nb 4.6 call engine.gas_limit_override (Used only by Aura) TODO added in new version
// t_nb 4.7 for every transaction call engine.verify_transaction_basic
for t in &block.transactions {
engine.verify_transaction_basic(t, &block.header)?;
}
@ -90,7 +100,7 @@ pub fn verify_block_basic(
Ok(())
}
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
// t_nb 5.0 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(
@ -100,8 +110,10 @@ pub fn verify_block_unordered(
) -> Result<PreverifiedBlock, Error> {
let header = block.header;
if check_seal {
// t_nb 5.1
engine.verify_block_unordered(&header)?;
for uncle in &block.uncles {
// t_nb 5.2
engine.verify_block_unordered(uncle)?;
}
}
@ -112,13 +124,16 @@ pub fn verify_block_unordered(
None
};
// t_nb 5.3 iterate over all transactions
let transactions = block
.transactions
.into_iter()
.map(|t| {
// t_nb 5.3.1 call verify_unordered. Check signatures and calculate address
let t = engine.verify_transaction_unordered(t, &header)?;
// t_nb 5.3.2 check if nonce is more then max nonce (EIP-168 and EIP169)
if let Some(max_nonce) = nonce_cap {
if t.nonce >= max_nonce {
if t.tx().nonce >= max_nonce {
return Err(BlockError::TooManyTransactions(t.sender()).into());
}
}
@ -146,7 +161,7 @@ pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> {
pub client: &'a C,
}
/// Phase 3 verification. Check block information against parent and uncles.
/// t_nb 6.3 Phase 3 verification. Check block information against parent and uncles.
pub fn verify_block_family<C: BlockInfo + CallContract>(
header: &Header,
parent: &Header,
@ -154,6 +169,7 @@ pub fn verify_block_family<C: BlockInfo + CallContract>(
do_full: Option<FullFamilyParams<C>>,
) -> Result<(), Error> {
// TODO: verify timestamp
// t_nb 6.3.1 verify parent
verify_parent(&header, &parent, engine)?;
engine.verify_block_family(&header, &parent)?;
@ -162,8 +178,10 @@ pub fn verify_block_family<C: BlockInfo + CallContract>(
None => return Ok(()),
};
// t_nb 6.3.2 verify uncles
verify_uncles(params.block, params.block_provider, engine)?;
// t_nb 6.3.3 verify all transactions
for tx in &params.block.transactions {
// transactions are verified against the parent header since the current
// state wasn't available when the tx was created
@ -475,7 +493,16 @@ fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Re
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()));
let expected_root = ordered_trie_root(tx.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// This is already checked in Unverified structure and that is why we are okay to asume that data is valid.
r.data().expect(
"Unverified block should already check if raw list of transactions is valid",
)
}
}));
if &expected_root != block.header.transactions_root() {
bail!(BlockError::InvalidTransactionsRoot(Mismatch {
expected: expected_root,
@ -513,7 +540,7 @@ mod tests {
use types::{
encoded,
log_entry::{LocalizedLogEntry, LogEntry},
transaction::{Action, SignedTransaction, Transaction, UnverifiedTransaction},
transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
};
fn check_ok(result: Result<(), Error>) {
@ -746,34 +773,34 @@ mod tests {
let keypair = Random.generate().unwrap();
let tr1 = Transaction {
let tr1 = TypedTransaction::Legacy(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 {
let tr2 = TypedTransaction::Legacy(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 {
let tr3 = TypedTransaction::Legacy(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()];
@ -816,16 +843,10 @@ mod tests {
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::<UnverifiedTransaction>(t)),
);
let eip86_transactions_root = ordered_trie_root(
eip86_transactions
.iter()
.map(|t| ::rlp::encode::<UnverifiedTransaction>(t)),
);
let good_transactions_root =
ordered_trie_root(good_transactions.iter().map(|t| t.encode()));
let eip86_transactions_root =
ordered_trie_root(eip86_transactions.iter().map(|t| t.encode()));
let mut parent = good.clone();
parent.set_number(9);
@ -1096,14 +1117,14 @@ mod tests {
let keypair = Random.generate().unwrap();
let bad_transactions: Vec<_> = (0..3)
.map(|i| {
Transaction {
TypedTransaction::Legacy(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();

View File

@ -529,6 +529,7 @@ impl ChainNotify for EthSync {
}
}
// t_nb 11.4
fn new_blocks(&self, new_blocks: NewBlocks) {
if new_blocks.has_more_blocks_to_import {
return;
@ -554,16 +555,14 @@ impl ChainNotify for EthSync {
fn start(&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),
}
},
_ => {},
}
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),
},
_ => {}
}
self.network
.register_protocol(

View File

@ -241,7 +241,7 @@ impl BlockDownloader {
self.last_round_start_hash = start_hash.clone();
self.imported_this_round = None;
self.round_parents = VecDeque::new();
self.target_hash = None;
//self.target_hash = None; // target_hash is only used for old (ancient) block download. And once set should not be reseted in any way.
self.retract_step = 1;
}
@ -516,8 +516,24 @@ impl BlockDownloader {
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 (best, best_hash) = match self.block_set {
BlockSet::NewBlocks => (
io.chain().chain_info().best_block_number,
io.chain().chain_info().best_block_hash,
),
BlockSet::OldBlocks => {
if let (Some(best), Some(best_hash)) = (
io.chain().chain_info().ancient_block_number,
io.chain().chain_info().ancient_block_hash,
) {
(best, best_hash) // best ancient block number and hash.
} else {
// None on ancient block/hash means that all ancient are already downloaded and stored in DB.
self.state = State::Complete;
return;
}
}
};
let oldest_reorg = io.chain().pruning_info().earliest_state;
if self.block_set == BlockSet::NewBlocks && best > start && start < oldest_reorg
{
@ -531,7 +547,7 @@ impl BlockDownloader {
} 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);
info!("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;
@ -547,11 +563,9 @@ impl BlockDownloader {
);
}
None => {
debug_sync!(
self,
info!(
"Could not revert to previous block, last: {} ({})",
start,
self.last_imported_hash
start, self.last_imported_hash
);
self.reset_to_block(&best_hash, best);
}
@ -661,7 +675,10 @@ impl BlockDownloader {
if self.target_hash.as_ref().map_or(false, |t| t == &h) {
self.state = State::Complete;
trace_sync!(self, "Sync target reached");
info!(
"Sync target {:?} for old blocks reached. Syncing ancient blocks finished.",
self.target_hash
);
return download_action;
}
@ -762,7 +779,7 @@ mod tests {
use triehash_ethereum::ordered_trie_root;
use types::{
header::Header as BlockHeader,
transaction::{SignedTransaction, Transaction},
transaction::{SignedTransaction, Transaction, TypedTransaction},
};
fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader {
@ -778,7 +795,7 @@ mod tests {
fn dummy_signed_tx() -> SignedTransaction {
let keypair = Random.generate().unwrap();
Transaction::default().sign(keypair.secret(), None)
TypedTransaction::Legacy(Transaction::default()).sign(keypair.secret(), None)
}
fn import_headers(
@ -949,8 +966,16 @@ mod tests {
::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_strem = RlpStream::new();
SignedTransaction::rlp_append_list(&mut rlp_strem, &[dummy_signed_tx()]);
let txs = rlp_strem.drain();
let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
r.data().expect("It is expected that raw rlp list is valid")
}
}));
let mut rlp = RlpStream::new_list(2);
rlp.append_raw(&txs, 1);
@ -1019,14 +1044,20 @@ mod tests {
// 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.
// all receipts as byte blobs, so it does not matter. It is just important that they are
// represended as list (0xc1) so that they passes as legacy list
let receipts_rlp = if i < 2 {
encode_list(&[0u32])
vec![0xC1, 0]
} else {
encode_list(&[i as u32])
vec![0xC1, i as u8]
};
let receipts_root =
ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| r.as_raw()));
let receipts_root = ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
r.data().expect("expect proper test data")
}
}));
receipts.push(receipts_rlp);
// Construct the block header.

View File

@ -23,7 +23,10 @@ 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};
use types::{
header::Header as BlockHeader,
transaction::{TypedTransaction, UnverifiedTransaction},
};
known_heap_size!(0, HeaderId);
@ -65,7 +68,7 @@ impl SyncBody {
let result = SyncBody {
transactions_bytes: transactions_rlp.as_raw().to_vec(),
transactions: transactions_rlp.as_list()?,
transactions: TypedTransaction::decode_rlp_list(&transactions_rlp)?,
uncles_bytes: uncles_rlp.as_raw().to_vec(),
uncles: uncles_rlp.as_list()?,
};
@ -454,11 +457,14 @@ impl BlockCollection {
fn insert_body(&mut self, body: SyncBody) -> Result<H256, network::Error> {
let header_id = {
let tx_root = ordered_trie_root(
Rlp::new(&body.transactions_bytes)
.iter()
.map(|r| r.as_raw()),
);
let tx_root = ordered_trie_root(Rlp::new(&body.transactions_bytes).iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// this list is already decoded and passed validation, for this we are okay to expect proper data
r.data().expect("Expect raw transaction list to be valid")
}
}));
let uncles = keccak(&body.uncles_bytes);
HeaderId {
transactions_root: tx_root,
@ -491,7 +497,22 @@ impl BlockCollection {
fn insert_receipt(&mut self, r: Bytes) -> Result<Vec<H256>, network::Error> {
let receipt_root = {
let receipts = Rlp::new(&r);
ordered_trie_root(receipts.iter().map(|r| r.as_raw()))
//check receipts data before calculating trie root
let mut temp_receipts: Vec<&[u8]> = Vec::new();
for receipt_byte in receipts.iter() {
if receipt_byte.is_list() {
temp_receipts.push(receipt_byte.as_raw())
} else {
temp_receipts.push(
receipt_byte
.data()
.map_err(|e| network::ErrorKind::Rlp(e))?,
);
}
}
// calculate trie root and use it as hash
ordered_trie_root(temp_receipts.iter())
};
self.downloading_receipts.remove(&receipt_root);
match self.receipt_ids.entry(receipt_root) {

View File

@ -155,6 +155,7 @@ impl SyncHandler {
trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id);
return Ok(());
}
// t_nb 1.0 decode RLP
let block = Unverified::from_rlp(r.at(0)?.as_raw().to_vec())?;
let hash = block.header.hash();
let number = block.header.number();
@ -166,7 +167,9 @@ impl SyncHandler {
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
// t_nb 1.1 check new block diffuculty it can be found as second item in RLP and update peer diffuculty
let parent_td = difficulty.checked_sub(*block.header.difficulty());
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
if peer
.difficulty
@ -181,6 +184,7 @@ impl SyncHandler {
peer.latest_hash = *parent_hash;
}
// t_nb 1.2 if block number is to older then 20 dont process it
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);
@ -833,9 +837,12 @@ impl SyncHandler {
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();
for i in r.iter() {
let tx = if i.is_list() {
i.as_raw().to_vec() // legacy transaction. just add it raw
} else {
i.data()?.to_vec() // typed transaction. remove header from start and send only payload.
};
transactions.push(tx);
}
io.chain().queue_transactions(transactions, peer_id);

View File

@ -969,15 +969,17 @@ impl ChainSync {
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);
info!(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);
trace!(target: "sync", "Downloader target for old blocks is set to {:?}", hash);
downloader.set_target(&hash);
} else {
trace!(target: "sync", "Downloader target could not be found");
}
self.old_blocks = Some(downloader);
}
@ -1114,10 +1116,18 @@ impl ChainSync {
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)) {
SyncRequester::request_blocks(self, io, peer_id, request, BlockSet::OldBlocks);
return;
}
let mut is_complete = false;
if let Some(old_blocks) = self.old_blocks.as_mut() {
if let Some(request) = old_blocks.request_blocks(peer_id, io, num_active_peers) {
SyncRequester::request_blocks(self, io, peer_id, request, BlockSet::OldBlocks);
return;
}
is_complete = old_blocks.is_complete();
}
if is_complete { // if old_blocks is in complete state, set it to None.
self.old_blocks = None;
}
} else {
trace!(
target: "sync",
@ -1344,7 +1354,7 @@ impl ChainSync {
},
SyncState::SnapshotWaiting => match io.snapshot_service().restoration_status() {
RestorationStatus::Inactive => {
trace!(target:"sync", "Snapshot restoration is complete");
info!(target:"sync", "Snapshot restoration is complete");
self.restart(io);
}
RestorationStatus::Initializing { .. } => {
@ -1421,7 +1431,7 @@ impl ChainSync {
self.check_resume(io);
}
/// called when block is imported to chain - propagates the blocks and updates transactions sent to peers
// t_nb 11.4 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,
@ -1437,16 +1447,18 @@ impl ChainSync {
if !is_syncing || !sealed.is_empty() || !proposed.is_empty() {
trace!(target: "sync", "Propagating blocks, state={:?}", self.state);
// t_nb 11.4.1 propagate latest blocks
SyncPropagator::propagate_latest_blocks(self, io, sealed);
// t_nb 11.4.4 propagate proposed blocks
SyncPropagator::propagate_proposed_blocks(self, io, proposed);
}
if !invalid.is_empty() {
trace!(target: "sync", "Bad blocks in the queue, restarting");
info!(target: "sync", "Bad blocks in the queue, restarting sync");
self.restart(io);
}
if !is_syncing && !enacted.is_empty() && !self.peers.is_empty() {
// Select random peer to re-broadcast transactions to.
// t_nb 11.4.5 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| {

View File

@ -21,7 +21,7 @@ use ethereum_types::H256;
use fastmap::H256FastSet;
use network::{client_version::ClientCapabilities, PeerId};
use rand::Rng;
use rlp::{Encodable, RlpStream};
use rlp::RlpStream;
use sync_io::SyncIo;
use types::{blockchain_info::BlockChainInfo, transaction::SignedTransaction, BlockNumber};
@ -39,7 +39,7 @@ use super::{
pub struct SyncPropagator;
impl SyncPropagator {
/// propagates latest block to a set of peers
// t_nb 11.4.3 propagates latest block to a set of peers
pub fn propagate_blocks(
sync: &mut ChainSync,
chain_info: &BlockChainInfo,
@ -72,7 +72,7 @@ impl SyncPropagator {
sent
}
/// propagates new known hashes to all peers
// t_nb 11.4.2 propagates new known hashes to all peers
pub fn propagate_new_hashes(
sync: &mut ChainSync,
chain_info: &BlockChainInfo,
@ -121,7 +121,7 @@ impl SyncPropagator {
let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions
.iter()
.map(|tx| tx.signed())
.partition(|tx| !tx.gas_price.is_zero());
.partition(|tx| !tx.tx().gas_price.is_zero());
// usual transactions could be propagated to all peers
let mut affected_peers = HashSet::new();
@ -171,7 +171,7 @@ impl SyncPropagator {
let all_transactions_rlp = {
let mut packet = RlpStream::new_list(transactions.len());
for tx in &transactions {
packet.append(&**tx);
tx.rlp_append(&mut packet);
}
packet.out()
};
@ -238,13 +238,8 @@ impl SyncPropagator {
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,
);
let appended =
packet.append_raw_checked(&tx.encode(), 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());
@ -279,6 +274,7 @@ impl SyncPropagator {
sent_to_peers
}
// t_nb 11.4.1 propagate latest blocks 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()
@ -287,15 +283,19 @@ impl SyncPropagator {
{
let peers = sync.get_lagging_peers(&chain_info);
if sealed.is_empty() {
// t_nb 11.4.2
let hashes = SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers);
let peers = ChainSync::select_random_peers(&peers);
// t_nb 11.4.3
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 {
// t_nb 11.4.3
SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers);
// t_nb 11.4.2
SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers);
trace!(target: "sync", "Sent sealed block to all peers");
};
@ -303,7 +303,7 @@ impl SyncPropagator {
sync.last_sent_block_number = chain_info.best_block_number;
}
/// Distribute valid proposed blocks to subset of current peers.
// t_nb 11.4.4 Distribute valid proposed blocks to subset of current peers. (if there is any proposed)
pub fn propagate_proposed_blocks(
sync: &mut ChainSync,
io: &mut dyn SyncIo,
@ -368,6 +368,7 @@ mod tests {
use rlp::Rlp;
use std::collections::VecDeque;
use tests::{helpers::TestIo, snapshot::TestSnapshotService};
use types::transaction::TypedTransaction;
use super::{
super::{tests::*, *},
@ -702,7 +703,9 @@ mod tests {
return None;
}
rlp.at(0).ok().and_then(|r| r.as_val().ok())
rlp.at(0)
.ok()
.and_then(|r| TypedTransaction::decode_rlp(&r).ok())
})
.collect();
assert_eq!(sent_transactions.len(), 2);

View File

@ -26,18 +26,18 @@ use ethkey::{KeyPair, Secret};
use hash::keccak;
use io::{IoChannel, IoHandler};
use std::sync::Arc;
use types::transaction::{Action, PendingTransaction, Transaction};
use types::transaction::{Action, PendingTransaction, Transaction, TypedTransaction};
use SyncConfig;
fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction {
let signed = Transaction {
let signed = TypedTransaction::Legacy(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)
}

View File

@ -14,6 +14,9 @@ parity-bytes = "0.1"
rlp = { version = "0.3.0", features = ["ethereum"] }
rlp_derive = { path = "../../util/rlp-derive" }
unexpected = { path = "../../util/unexpected" }
serde = "1.0"
serde_json = "1.0"
serde_repr = "0.1"
[dev-dependencies]
rustc-hex = "1.0"

View File

@ -35,7 +35,7 @@ use bytes::Bytes;
use header::Header;
use rlp::{Decodable, DecoderError, Rlp, RlpStream};
use transaction::UnverifiedTransaction;
use transaction::{TypedTransaction, UnverifiedTransaction};
/// A block, encoded as it is on the block chain.
#[derive(Default, Debug, Clone, PartialEq)]
@ -53,7 +53,7 @@ impl Block {
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);
TypedTransaction::rlp_append_list(&mut block_rlp, &self.transactions);
block_rlp.append_list(&self.uncles);
block_rlp.out()
}
@ -69,7 +69,7 @@ impl Decodable for Block {
}
Ok(Block {
header: rlp.val_at(0)?,
transactions: rlp.list_at(1)?,
transactions: TypedTransaction::decode_rlp_list(&rlp.at(1)?)?,
uncles: rlp.list_at(2)?,
})
}

View File

@ -41,6 +41,7 @@ extern crate heapsize;
extern crate keccak_hash as hash;
extern crate parity_bytes as bytes;
extern crate rlp;
extern crate serde_repr;
extern crate unexpected;
#[macro_use]

View File

@ -16,9 +16,14 @@
//! Receipt
use super::transaction::TypedTxId;
use ethereum_types::{Address, Bloom, H160, H256, U256};
use heapsize::HeapSizeOf;
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
use rlp::{DecoderError, Rlp, RlpStream};
use std::{
convert::TryInto,
ops::{Deref, DerefMut},
};
use log_entry::{LocalizedLogEntry, LogEntry};
use BlockNumber;
@ -36,7 +41,7 @@ pub enum TransactionOutcome {
/// Information describing execution of a transaction.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Receipt {
pub struct LegacyReceipt {
/// 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.
@ -47,10 +52,9 @@ pub struct Receipt {
pub outcome: TransactionOutcome,
}
impl Receipt {
/// Create a new receipt.
impl LegacyReceipt {
pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec<LogEntry>) -> Self {
Self {
LegacyReceipt {
gas_used,
log_bloom: logs.iter().fold(Bloom::default(), |mut b, l| {
b.accrue_bloom(&l.bloom());
@ -60,10 +64,32 @@ impl Receipt {
outcome,
}
}
}
pub fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
match rlp.item_count()? {
3 => Ok(LegacyReceipt {
outcome: TransactionOutcome::Unknown,
gas_used: rlp.val_at(0)?,
log_bloom: rlp.val_at(1)?,
logs: rlp.list_at(2)?,
}),
4 => Ok(LegacyReceipt {
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()?)
}
},
}),
_ => Err(DecoderError::RlpIncorrectListLen),
}
}
impl Encodable for Receipt {
fn rlp_append(&self, s: &mut RlpStream) {
pub fn rlp_append(&self, s: &mut RlpStream) {
match self.outcome {
TransactionOutcome::Unknown => {
s.begin_list(3);
@ -83,42 +109,142 @@ impl Encodable for Receipt {
}
}
impl Decodable for Receipt {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
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)?,
})
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypedReceipt {
Legacy(LegacyReceipt),
AccessList(LegacyReceipt),
}
impl TypedReceipt {
/// Create a new receipt.
pub fn new(type_id: TypedTxId, legacy_receipt: LegacyReceipt) -> Self {
//curently we are using same receipt for both legacy and typed transaction
match type_id {
TypedTxId::AccessList => Self::AccessList(legacy_receipt),
TypedTxId::Legacy => Self::Legacy(legacy_receipt),
}
}
pub fn tx_type(&self) -> TypedTxId {
match self {
Self::Legacy(_) => TypedTxId::Legacy,
Self::AccessList(_) => TypedTxId::AccessList,
}
}
pub fn receipt(&self) -> &LegacyReceipt {
match self {
Self::Legacy(receipt) => receipt,
Self::AccessList(receipt) => receipt,
}
}
pub fn receipt_mut(&mut self) -> &mut LegacyReceipt {
match self {
Self::Legacy(receipt) => receipt,
Self::AccessList(receipt) => receipt,
}
}
fn decode(tx: &[u8]) -> Result<Self, DecoderError> {
if tx.is_empty() {
// at least one byte needs to be present
return Err(DecoderError::RlpIncorrectListLen);
}
let id = tx[0].try_into();
if id.is_err() {
return Err(DecoderError::Custom("Unknown transaction"));
}
//other transaction types
match id.unwrap() {
TypedTxId::AccessList => {
let rlp = Rlp::new(&tx[1..]);
Ok(Self::AccessList(LegacyReceipt::decode(&rlp)?))
}
TypedTxId::Legacy => Ok(Self::Legacy(LegacyReceipt::decode(&Rlp::new(tx))?)),
}
}
pub fn decode_rlp(rlp: &Rlp) -> Result<Self, DecoderError> {
if rlp.is_list() {
//legacy transaction wrapped around RLP encoding
Ok(Self::Legacy(LegacyReceipt::decode(rlp)?))
} 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()?)
}
},
})
Self::decode(rlp.data()?)
}
}
pub fn decode_rlp_list(rlp: &Rlp) -> Result<Vec<Self>, DecoderError> {
if !rlp.is_list() {
// at least one byte needs to be present
return Err(DecoderError::RlpIncorrectListLen);
}
let mut output = Vec::with_capacity(rlp.item_count()?);
for tx in rlp.iter() {
output.push(Self::decode_rlp(&tx)?);
}
Ok(output)
}
pub fn rlp_append(&self, s: &mut RlpStream) {
match self {
Self::Legacy(receipt) => receipt.rlp_append(s),
Self::AccessList(receipt) => {
let mut rlps = RlpStream::new();
receipt.rlp_append(&mut rlps);
s.append(&[&[TypedTxId::AccessList as u8], rlps.as_raw()].concat());
}
}
}
pub fn rlp_append_list(s: &mut RlpStream, list: &[TypedReceipt]) {
s.begin_list(list.len());
for rec in list.iter() {
rec.rlp_append(s)
}
}
pub fn encode(&self) -> Vec<u8> {
match self {
Self::Legacy(receipt) => {
let mut s = RlpStream::new();
receipt.rlp_append(&mut s);
s.drain()
}
Self::AccessList(receipt) => {
let mut rlps = RlpStream::new();
receipt.rlp_append(&mut rlps);
[&[TypedTxId::AccessList as u8], rlps.as_raw()].concat()
}
}
}
}
impl HeapSizeOf for Receipt {
impl Deref for TypedReceipt {
type Target = LegacyReceipt;
fn deref(&self) -> &Self::Target {
self.receipt()
}
}
impl DerefMut for TypedReceipt {
fn deref_mut(&mut self) -> &mut LegacyReceipt {
self.receipt_mut()
}
}
impl HeapSizeOf for TypedReceipt {
fn heap_size_of_children(&self) -> usize {
self.logs.heap_size_of_children()
self.receipt().logs.heap_size_of_children()
}
}
/// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct RichReceipt {
/// Transaction type
pub transaction_type: TypedTxId,
/// Transaction hash.
pub transaction_hash: H256,
/// Transaction index.
@ -146,6 +272,8 @@ pub struct RichReceipt {
/// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct LocalizedReceipt {
/// Transaction type
pub transaction_type: TypedTxId,
/// Transaction hash.
pub transaction_hash: H256,
/// Transaction index.
@ -176,59 +304,91 @@ pub struct LocalizedReceipt {
#[cfg(test)]
mod tests {
use super::{Receipt, TransactionOutcome};
use super::{LegacyReceipt, TransactionOutcome, TypedReceipt, TypedTxId};
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],
}],
let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::Unknown,
0x40cae.into(),
vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
topics: vec![],
data: vec![0u8; 32],
}],
),
);
assert_eq!(&::rlp::encode(&r)[..], &expected[..]);
assert_eq!(r.encode(), expected);
}
#[test]
fn test_basic() {
fn test_basic_legacy() {
let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new(
TransactionOutcome::StateRoot(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StateRoot(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
),
0x40cae.into(),
vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
topics: vec![],
data: vec![0u8; 32],
}],
),
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");
let encoded = r.encode();
assert_eq!(encoded, expected);
let decoded = TypedReceipt::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r);
}
#[test]
fn test_basic_access_list() {
let expected = ::rustc_hex::FromHex::from_hex("01f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = TypedReceipt::new(
TypedTxId::AccessList,
LegacyReceipt::new(
TransactionOutcome::StateRoot(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
),
0x40cae.into(),
vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
topics: vec![],
data: vec![0u8; 32],
}],
),
);
let encoded = r.encode();
assert_eq!(&encoded, &expected);
let decoded = TypedReceipt::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 r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StatusCode(0),
0x40cae.into(),
vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
topics: vec![],
data: vec![0u8; 32],
}],
),
);
let encoded = ::rlp::encode(&r);
let encoded = r.encode();
assert_eq!(&encoded[..], &expected[..]);
let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed");
let decoded = TypedReceipt::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r);
}
}

View File

@ -84,6 +84,8 @@ pub enum Error {
TooBig,
/// Invalid RLP encoding
InvalidRlp(String),
/// Transaciton is still not enabled.
TransactionTypeNotEnabled,
}
impl From<ethkey::Error> for Error {
@ -133,6 +135,9 @@ impl fmt::Display for Error {
}
TooBig => "Transaction too big".into(),
InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err),
TransactionTypeNotEnabled => {
format!("Transaction type is not enabled for current block")
}
};
f.write_fmt(format_args!("Transaction error ({})", msg))

View File

@ -18,5 +18,6 @@
mod error;
mod transaction;
mod transaction_id;
pub use self::{error::Error, transaction::*};
pub use self::{error::Error, transaction::*, transaction_id::*};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
// Copyright 2020-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 <http://www.gnu.org/licenses/>.
//! Transaction Id.
use serde_repr::*;
use std::convert::TryFrom;
#[derive(Serialize_repr, Eq, Hash, Deserialize_repr, Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum TypedTxId {
AccessList = 0x01,
Legacy = 0x80, // With 0x80 we are sure that all other types will not overlap
}
impl Default for TypedTxId {
fn default() -> TypedTxId {
TypedTxId::Legacy
}
}
impl TryFrom<u8> for TypedTxId {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
match v {
x if x == TypedTxId::AccessList as u8 => Ok(TypedTxId::AccessList),
x if (x & 0x80) != 0x00 => Ok(TypedTxId::Legacy),
_ => Err(()),
}
}
}

View File

@ -21,7 +21,7 @@ use bytes::Bytes;
use ethereum_types::H256;
use hash::keccak;
use header::Header;
use transaction::{LocalizedTransaction, UnverifiedTransaction};
use transaction::{LocalizedTransaction, TypedTransaction, UnverifiedTransaction};
use views::{HeaderView, TransactionView};
/// View onto block rlp.
@ -77,7 +77,12 @@ impl<'a> BlockView<'a> {
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<UnverifiedTransaction> {
self.rlp.list_at(1)
TypedTransaction::decode_rlp_list(&self.rlp.at(1).rlp).unwrap_or_else(|e| {
panic!(
"block transactions, view rlp is trusted and should be valid: {:?}",
e
)
})
}
/// Return List of transactions with additional localization info.
@ -126,10 +131,14 @@ impl<'a> BlockView<'a> {
/// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> {
self.transactions_rlp()
.iter()
.nth(index)
.map(|rlp| rlp.as_val())
self.transactions_rlp().iter().nth(index).map(|rlp| {
TypedTransaction::decode_rlp(&rlp.rlp).unwrap_or_else(|e| {
panic!(
"block transaction_at, view rlp is trusted and should be valid.{:?}",
e
)
})
})
}
/// Returns localized transaction at given index.

View File

@ -21,7 +21,7 @@ use bytes::Bytes;
use ethereum_types::H256;
use hash::keccak;
use header::Header;
use transaction::{LocalizedTransaction, UnverifiedTransaction};
use transaction::{LocalizedTransaction, TypedTransaction, UnverifiedTransaction};
use views::{HeaderView, TransactionView};
use BlockNumber;
@ -58,7 +58,12 @@ impl<'a> BodyView<'a> {
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<UnverifiedTransaction> {
self.rlp.list_at(0)
TypedTransaction::decode_rlp_list(&self.rlp.at(0).rlp).unwrap_or_else(|e| {
panic!(
"body transactions, view rlp is trusted and should be valid: {:?}",
e
)
})
}
/// Return List of transactions with additional localization info.
@ -107,10 +112,14 @@ impl<'a> BodyView<'a> {
/// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> {
self.transactions_rlp()
.iter()
.nth(index)
.map(|rlp| rlp.as_val())
self.transactions_rlp().iter().nth(index).map(|rlp| {
TypedTransaction::decode_rlp(&rlp.rlp).unwrap_or_else(|e| {
panic!(
"body transaction_a, view rlp is trusted and should be valid: {:?}",
e
)
})
})
}
/// Returns localized transaction at given index.

View File

@ -0,0 +1,260 @@
use ethereum_types::{Address, H256};
use std::{
borrow::Borrow,
collections::HashMap,
hash::{Hash, Hasher},
};
use std::{cell::RefCell, rc::Rc};
// Implementation of a hasheable borrowed pair
trait KeyPair<A, B> {
fn a(&self) -> &A;
fn b(&self) -> &B;
}
impl<'a, A, B> Borrow<dyn KeyPair<A, B> + 'a> for (A, B)
where
A: Eq + Hash + 'a,
B: Eq + Hash + 'a,
{
fn borrow(&self) -> &(dyn KeyPair<A, B> + 'a) {
self
}
}
impl<A: Hash, B: Hash> Hash for (dyn KeyPair<A, B> + '_) {
fn hash<H: Hasher>(&self, state: &mut H) {
self.a().hash(state);
self.b().hash(state);
}
}
impl<A: Eq, B: Eq> PartialEq for (dyn KeyPair<A, B> + '_) {
fn eq(&self, other: &Self) -> bool {
self.a() == other.a() && self.b() == other.b()
}
}
impl<A: Eq, B: Eq> Eq for (dyn KeyPair<A, B> + '_) {}
impl<A, B> KeyPair<A, B> for (A, B) {
fn a(&self) -> &A {
&self.0
}
fn b(&self) -> &B {
&self.1
}
}
impl<A, B> KeyPair<A, B> for (&A, &B) {
fn a(&self) -> &A {
self.0
}
fn b(&self) -> &B {
self.1
}
}
#[derive(Debug)]
struct Journal {
enabled: bool,
last_id: usize,
addresses: HashMap<Address, usize>,
storage_keys: HashMap<(Address, H256), usize>,
}
#[derive(Debug)]
pub struct AccessList {
id: usize,
journal: Rc<RefCell<Journal>>,
}
impl Clone for AccessList {
fn clone(&self) -> Self {
let mut journal = self.journal.as_ref().borrow_mut();
let id = journal.last_id + 1;
journal.last_id = id;
Self {
id: id,
journal: self.journal.clone(),
}
}
}
impl Default for AccessList {
fn default() -> Self {
AccessList::new(false)
}
}
impl std::fmt::Display for AccessList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let journal = self.journal.as_ref().borrow();
for (addr, id) in journal.addresses.iter() {
write!(f, "| ADDR {} -> {}\n", addr, id)?;
}
for ((addr, slot), id) in journal.storage_keys.iter() {
write!(f, "| SLOT {}:{} -> {}\n", addr, slot, id)?;
}
Ok(())
}
}
impl AccessList {
/// Returns if the list is enabled
pub fn new(enabled: bool) -> Self {
let journal = Journal {
enabled,
last_id: 0,
addresses: HashMap::new(),
storage_keys: HashMap::new(),
};
Self {
id: 0,
journal: Rc::new(RefCell::new(journal)),
}
}
/// Returns if the list is enabled
pub fn is_enabled(&self) -> bool {
let journal = self.journal.as_ref().borrow();
journal.enabled
}
/// Enable the access list control
pub fn enable(&mut self) {
let mut journal = self.journal.as_ref().borrow_mut();
journal.enabled = true;
}
/// Checks if contains an storage key
pub fn contains_storage_key(&self, address: &Address, key: &H256) -> bool {
let journal = self.journal.as_ref().borrow();
if journal.enabled {
journal
.storage_keys
.contains_key(&(address, key) as &dyn KeyPair<Address, H256>)
} else {
false
}
}
/// Inserts a storage key
pub fn insert_storage_key(&mut self, address: Address, key: H256) {
let mut journal = self.journal.as_ref().borrow_mut();
if journal.enabled
&& !journal
.storage_keys
.contains_key(&(address, key) as &dyn KeyPair<Address, H256>)
{
journal.storage_keys.insert((address, key), self.id);
}
}
/// Checks if contains an address
pub fn contains_address(&self, address: &Address) -> bool {
let journal = self.journal.as_ref().borrow();
if journal.enabled {
journal.addresses.contains_key(&address)
} else {
false
}
}
/// Inserts an address
pub fn insert_address(&mut self, address: Address) {
let mut journal = self.journal.as_ref().borrow_mut();
if journal.enabled && !journal.addresses.contains_key(&address) {
journal.addresses.insert(address, self.id);
}
}
/// Removes all changes in journal
pub fn rollback(&self) {
let mut journal = self.journal.as_ref().borrow_mut();
// `id < self.id` instead `id != self.if` is to take care about recursive calls
journal.addresses.retain(|_, id| *id < self.id);
journal.storage_keys.retain(|_, id| *id < self.id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_accesslist_is_disabled() {
let access_list = AccessList::default();
assert_eq!(false, access_list.is_enabled());
}
#[test]
fn default_disabled_accesslist_does_nothing() {
let mut access_list = AccessList::default();
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
assert_eq!(false, access_list.contains_address(&Address::from(1)));
assert_eq!(
false,
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
);
}
#[test]
fn default_enabled_accesslist_registers() {
let mut access_list = AccessList::default();
access_list.enable();
assert_eq!(true, access_list.is_enabled());
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
assert_eq!(true, access_list.contains_address(&Address::from(1)));
assert_eq!(
true,
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
);
}
#[test]
fn cloned_accesslist_registers_in_parent() {
let mut access_list = AccessList::default();
access_list.enable();
assert_eq!(true, access_list.is_enabled());
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
let access_list_call = access_list.clone();
assert_eq!(true, access_list_call.contains_address(&Address::from(1)));
assert_eq!(
true,
access_list_call.contains_storage_key(&Address::from(2), &H256::from(3))
);
access_list.insert_address(Address::from(4));
assert_eq!(true, access_list_call.contains_address(&Address::from(4)));
assert_eq!(true, access_list.contains_address(&Address::from(4)));
}
#[test]
fn cloned_accesslist_rollbacks_in_parent() {
let mut access_list = AccessList::default();
access_list.enable();
assert_eq!(true, access_list.is_enabled());
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
let mut access_list_call = access_list.clone();
access_list_call.insert_address(Address::from(1));
access_list_call.insert_storage_key(Address::from(2), H256::from(3));
access_list_call.insert_address(Address::from(4));
let mut access_list_call_call = access_list.clone();
access_list_call_call.insert_address(Address::from(1));
access_list_call_call.insert_storage_key(Address::from(2), H256::from(3));
access_list_call_call.insert_address(Address::from(5));
access_list_call_call.insert_storage_key(Address::from(6), H256::from(7));
access_list_call.rollback();
assert_eq!(true, access_list.contains_address(&Address::from(1)));
assert_eq!(false, access_list.contains_address(&Address::from(4)));
assert_eq!(false, access_list.contains_address(&Address::from(5)));
assert_eq!(
true,
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
);
assert_eq!(
false,
access_list.contains_storage_key(&Address::from(6), &H256::from(7))
);
}
}

View File

@ -15,13 +15,13 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Evm input params.
use super::access_list::AccessList;
use bytes::Bytes;
use call_type::CallType;
use ethereum_types::{Address, H256, U256};
use ethjson;
use hash::{keccak, KECCAK_EMPTY};
use call_type::CallType;
use std::sync::Arc;
/// Transaction value
@ -90,6 +90,8 @@ pub struct ActionParams {
pub call_type: CallType,
/// Param types encoding
pub params_type: ParamsType,
/// Current access list
pub access_list: AccessList,
}
impl Default for ActionParams {
@ -108,6 +110,7 @@ impl Default for ActionParams {
data: None,
call_type: CallType::None,
params_type: ParamsType::Separate,
access_list: AccessList::default(),
}
}
}
@ -131,6 +134,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
false => CallType::Call,
}, // TODO @debris is this correct?
params_type: ParamsType::Separate,
access_list: AccessList::default(),
}
}
}

View File

@ -101,6 +101,9 @@ pub trait Ext {
trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind>;
/// Returns the address that will be created in the create call
fn calc_address(&self, code: &[u8], address: CreateContractAddress) -> Option<Address>;
/// Message call.
///
/// Returns Err, if we run out of gas.
@ -184,4 +187,19 @@ pub trait Ext {
/// Check if running in static context.
fn is_static(&self) -> bool;
/// Returns if the list is enabled
fn al_is_enabled(&self) -> bool;
/// Checks if contains an storage key
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool;
/// Inserts an storage key into the list
fn al_insert_storage_key(&mut self, address: Address, key: H256);
/// Checks if contains an address
fn al_contains_address(&self, address: &Address) -> bool;
/// Inserts an address into the list
fn al_insert_address(&mut self, address: Address);
}

View File

@ -23,16 +23,18 @@ extern crate parity_bytes as bytes;
extern crate patricia_trie_ethereum as ethtrie;
extern crate rlp;
pub mod access_list;
mod action_params;
mod call_type;
mod env_info;
mod error;
mod ext;
mod return_data;
mod schedule;
pub mod schedule;
pub mod tests;
pub use access_list::AccessList;
pub use action_params::{ActionParams, ActionValue, ParamsType};
pub use call_type::CallType;
pub use env_info::{EnvInfo, LastHashes};
@ -42,7 +44,7 @@ pub use return_data::{GasLeft, ReturnData};
pub use schedule::{CleanDustMode, Schedule, WasmCosts};
/// Virtual Machine interface
pub trait Exec: Send {
pub trait Exec {
/// 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.
@ -50,13 +52,13 @@ pub trait Exec: Send {
}
/// Resume call interface
pub trait ResumeCall: Send {
pub trait ResumeCall {
/// Resume an execution for call, returns back the Vm interface.
fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<dyn Exec>;
}
/// Resume create interface
pub trait ResumeCreate: Send {
pub trait ResumeCreate {
/// Resume an execution from create, returns back the Vm interface.
fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<dyn Exec>;
}

View File

@ -16,6 +16,19 @@
//! Cost schedule and other parameterisations for the EVM.
// Gas per non accessed address when sload
pub const EIP2929_COLD_SLOAD_COST: usize = 2100;
// Gas per non accessed address accessing account from other opcodes defined in EIP2929
pub const EIP2929_COLD_ACCOUNT_ACCESS_COST: usize = 2600;
// Gas per already accessed address
pub const EIP2929_WARM_STORAGE_READ_COST: usize = 100;
// Gas per sstore reset
pub const EIP2929_SSTORE_RESET_GAS: usize = 5000 - EIP2929_COLD_SLOAD_COST;
/// Gas per received storage key
pub const EIP2930_ACCESS_LIST_STORAGE_KEY_COST: usize = 1900;
/// Gas per received address
pub const EIP2930_ACCESS_LIST_ADDRESS_COST: usize = 2400;
/// Definition of the cost schedule and other parameterisations for the EVM.
#[derive(Debug)]
pub struct Schedule {
@ -63,6 +76,12 @@ pub struct Schedule {
pub create_gas: usize,
/// Gas price for `*CALL*` opcodes
pub call_gas: usize,
/// EIP-2929 COLD_SLOAD_COST
pub cold_sload_cost: usize,
/// EIP-2929 COLD_ACCOUNT_ACCESS_COST
pub cold_account_access_cost: usize,
/// EIP-2929 WARM_STORAGE_READ_COST
pub warm_storage_read_cost: usize,
/// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0`
pub call_stipend: usize,
/// Additional gas required for value transfer (`CALL|CALLCODE`)
@ -132,6 +151,10 @@ pub struct Schedule {
pub keep_unsigned_nonce: bool,
/// Wasm extra schedule settings, if wasm activated
pub wasm: Option<WasmCosts>,
/// Enable EIP-2929 rules
pub eip2929: bool,
/// Enable EIP-2930 rules for optional access list transactions. it depends on EIP-2929
pub eip2930: bool,
}
/// Wasm cost table
@ -245,6 +268,9 @@ impl Schedule {
log_topic_gas: 375,
create_gas: 32000,
call_gas: 700,
cold_account_access_cost: 0,
cold_sload_cost: 0,
warm_storage_read_cost: 0,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
@ -274,6 +300,8 @@ impl Schedule {
eip1706: false,
keep_unsigned_nonce: false,
wasm: None,
eip2929: false,
eip2930: false,
}
}
@ -290,7 +318,8 @@ impl 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.have_bitwise_shifting = true; // EIP 145
schedule.have_extcodehash = true; // EIP 1052
schedule
}
@ -310,6 +339,31 @@ impl Schedule {
pub fn new_berlin() -> Schedule {
let mut schedule = Self::new_istanbul();
schedule.have_subs = true; // EIP 2315
schedule
}
/// Schedule for the Yolov3 testnet of the Ethereum main net.
pub fn new_yolo3() -> Schedule {
let mut schedule = Self::new_istanbul();
schedule.have_subs = true; // EIP 2315
schedule.eip1283 = true;
schedule.eip2929 = true;
schedule.eip2930 = true;
schedule.cold_sload_cost = EIP2929_COLD_SLOAD_COST;
schedule.cold_account_access_cost = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.warm_storage_read_cost = EIP2929_WARM_STORAGE_READ_COST;
schedule.sload_gas = EIP2929_WARM_STORAGE_READ_COST;
schedule.call_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.balance_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodecopy_base_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodehash_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodesize_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.sstore_reset_gas = EIP2929_SSTORE_RESET_GAS;
schedule
}
@ -342,6 +396,9 @@ impl Schedule {
log_topic_gas: 375,
create_gas: 32000,
call_gas: 40,
cold_account_access_cost: 0,
cold_sload_cost: 0,
warm_storage_read_cost: 0,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
@ -371,6 +428,8 @@ impl Schedule {
eip1706: false,
keep_unsigned_nonce: false,
wasm: None,
eip2929: false,
eip2930: false,
}
}

View File

@ -19,6 +19,7 @@ use std::{
sync::Arc,
};
use crate::access_list::AccessList;
use bytes::Bytes;
use error::TrapKind;
use ethereum_types::{Address, H256, U256};
@ -75,6 +76,7 @@ pub struct FakeExt {
pub balances: HashMap<Address, U256>,
pub tracing: bool,
pub is_static: bool,
pub access_list: AccessList,
chain_id: u64,
}
@ -122,6 +124,19 @@ impl FakeExt {
ext
}
/// New fake externalities with YoloV2 schedule rules
pub fn new_yolo3(from: Address, to: Address, builtins: &[Address]) -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_yolo3();
ext.access_list.enable();
ext.access_list.insert_address(from);
ext.access_list.insert_address(to);
for builtin in builtins {
ext.access_list.insert_address(*builtin);
}
ext
}
/// Alter fake externalities to allow wasm
pub fn with_wasm(mut self) -> Self {
self.schedule.wasm = Some(Default::default());
@ -162,7 +177,7 @@ impl Ext for FakeExt {
}
fn balance(&self, address: &Address) -> Result<U256> {
Ok(self.balances[address])
Ok(self.balances.get(address).cloned().unwrap_or(U256::zero()))
}
fn blockhash(&mut self, number: &U256) -> H256 {
@ -191,6 +206,10 @@ impl Ext for FakeExt {
Ok(ContractCreateResult::Failed)
}
fn calc_address(&self, _code: &[u8], _address: CreateContractAddress) -> Option<Address> {
None
}
fn call(
&mut self,
gas: &U256,
@ -276,4 +295,24 @@ impl Ext for FakeExt {
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool {
self.tracing
}
fn al_is_enabled(&self) -> bool {
self.access_list.is_enabled()
}
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool {
self.access_list.contains_storage_key(address, key)
}
fn al_insert_storage_key(&mut self, address: Address, key: H256) {
self.access_list.insert_storage_key(address, key)
}
fn al_contains_address(&self, address: &Address) -> bool {
self.access_list.contains_address(address)
}
fn al_insert_address(&mut self, address: Address) {
self.access_list.insert_address(address)
}
}

View File

@ -5,11 +5,11 @@ EVM implementation for OpenEthereum.
### Usage
```
EVM implementation for OpenEthereum.
EVM implementation for Parity.
Copyright 2015-2020 Parity Technologies (UK) Ltd.
Usage:
openethereum-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only]
openethereum-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only --omit-storage-output --omit-memory-output]
openethereum-evm stats [options]
openethereum-evm stats-jsontests-vm <file>
openethereum-evm [options]
@ -30,16 +30,22 @@ 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, Berlin, Yolo3).
--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.
--std-json Display results in standardized JSON format.
--std-err-only With --std-json redirect to err output only.
--std-out-only With --std-json redirect to out output only.
--std-dump-json Display results in standardized JSON format
with additional state dump.
--json Display verbose results in JSON.
--std-json Display results in standardized JSON format.
--std-err-only With --std-json redirect to err output only.
--std-out-only With --std-json redirect to out output only.
--omit-storage-output With --std-json omit storage output.
--omit-memory-output With --std-json omit memory output.
--std-dump-json Display results in standardized JSON format
with additional state dump.
Display result state dump in standardized JSON format.
--chain CHAIN Chain spec file path.
-h, --help Display this message and exit.

View File

@ -0,0 +1,40 @@
// 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 <http://www.gnu.org/licenses/>.
//! Config used by display informants
#[derive(Default, Copy, Clone)]
pub struct Config {
omit_storage_output: bool,
omit_memory_output: bool,
}
impl Config {
pub fn new(omit_storage_output: bool, omit_memory_output: bool) -> Config {
Config {
omit_storage_output,
omit_memory_output,
}
}
pub fn omit_storage_output(&self) -> bool {
self.omit_storage_output
}
pub fn omit_memory_output(&self) -> bool {
self.omit_memory_output
}
}

View File

@ -18,11 +18,11 @@
use std::{collections::HashMap, mem};
use super::config::Config;
use bytes::ToPretty;
use display;
use ethcore::trace;
use ethereum_types::{H256, U256};
use display;
use info as vm;
/// JSON formatting informant.
@ -44,9 +44,16 @@ pub struct Informant {
subinfos: Vec<Informant>,
subdepth: usize,
unmatched: bool,
config: Config,
}
impl Informant {
pub fn new(config: Config) -> Informant {
let mut def = Informant::default();
def.config = config;
def
}
fn with_informant_in_depth<F: Fn(&mut Informant)>(
informant: &mut Informant,
depth: usize,
@ -67,17 +74,26 @@ impl Informant {
}
fn informant_trace(informant: &Informant, gas_used: U256) -> String {
let memory = if informant.config.omit_memory_output() {
"".to_string()
} else {
format!("0x{}", informant.memory.to_hex())
};
let storage = if informant.config.omit_storage_output() {
None
} else {
Some(&informant.storage)
};
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()),
"memory": memory,
"stack": informant.stack,
"storage": informant.storage,
"storage": storage,
"depth": informant.depth,
})
.to_string()
@ -85,7 +101,7 @@ impl Informant {
}
impl vm::Informant for Informant {
type Sink = ();
type Sink = Config;
fn before_test(&mut self, name: &str, action: &str) {
println!("{}", json!({"action": action, "test": name}));
@ -96,10 +112,10 @@ impl vm::Informant for Informant {
}
fn clone_sink(&self) -> Self::Sink {
()
self.config
}
fn finish(result: vm::RunResult<Self::Output>, _sink: &mut Self::Sink) {
fn finish(result: vm::RunResult<Self::Output>, config: &mut Self::Sink) {
match result {
Ok(success) => {
for trace in success.traces.unwrap_or_else(Vec::new) {
@ -115,8 +131,10 @@ impl vm::Informant for Informant {
println!("{}", success_msg)
}
Err(failure) => {
for trace in failure.traces.unwrap_or_else(Vec::new) {
println!("{}", trace);
if !config.omit_storage_output() {
for trace in failure.traces.unwrap_or_else(Vec::new) {
println!("{}", trace);
}
}
let failure_msg = json!({
@ -205,6 +223,7 @@ impl trace::VMTracer for Informant {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
let mut vm = Informant::default();
vm.config = informant.config;
vm.depth = informant.depth + 1;
vm.code = code.to_vec();
vm.gas_used = informant.gas_used;

View File

@ -18,6 +18,7 @@
use std::time::Duration;
pub mod config;
pub mod json;
pub mod simple;
pub mod std_json;

View File

@ -16,6 +16,7 @@
//! Simple VM output.
use super::config::Config;
use bytes::ToPretty;
use ethcore::trace;
@ -24,17 +25,25 @@ use info as vm;
/// Simple formatting informant.
#[derive(Default)]
pub struct Informant;
pub struct Informant {
config: Config,
}
impl Informant {
pub fn new(config: Config) -> Informant {
Informant { config }
}
}
impl vm::Informant for Informant {
type Sink = ();
type Sink = Config;
fn before_test(&mut self, name: &str, action: &str) {
println!("Test: {} ({})", name, action);
}
fn clone_sink(&self) -> Self::Sink {
()
self.config
}
fn finish(result: vm::RunResult<Self::Output>, _sink: &mut Self::Sink) {

View File

@ -18,11 +18,11 @@
use std::{collections::HashMap, io};
use super::config::Config;
use bytes::ToPretty;
use display;
use ethcore::{pod_state, trace};
use ethereum_types::{H256, U256};
use display;
use info as vm;
pub trait Writer: io::Write + Send + Sized {
@ -61,30 +61,39 @@ pub struct Informant<Trace, Out> {
subdepth: usize,
trace_sink: Trace,
out_sink: Out,
config: Config,
}
impl Default for Informant<io::Stderr, io::Stdout> {
fn default() -> Self {
Self::new(io::stderr(), io::stdout())
Self::new(io::stderr(), io::stdout(), Config::default())
}
}
impl Informant<io::Stdout, io::Stdout> {
/// std json informant using out only.
pub fn out_only() -> Self {
Self::new(io::stdout(), io::stdout())
pub fn out_only(config: Config) -> Self {
Self::new(io::stdout(), io::stdout(), config)
}
}
impl Informant<io::Stderr, io::Stderr> {
/// std json informant using err only.
pub fn err_only() -> Self {
Self::new(io::stderr(), io::stderr())
pub fn err_only(config: Config) -> Self {
Self::new(io::stderr(), io::stderr(), config)
}
}
impl Informant<io::Stderr, io::Stdout> {
pub fn new_default(config: Config) -> Self {
let mut informant = Self::default();
informant.config = config;
informant
}
}
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
pub fn new(trace_sink: Trace, out_sink: Out) -> Self {
pub fn new(trace_sink: Trace, out_sink: Out, config: Config) -> Self {
Informant {
code: Default::default(),
instruction: Default::default(),
@ -95,6 +104,7 @@ impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
subdepth: 0,
trace_sink,
out_sink,
config,
}
}
@ -133,7 +143,7 @@ impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
}
impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
type Sink = (Trace, Out);
type Sink = (Trace, Out, Config);
fn before_test(&mut self, name: &str, action: &str) {
let out_data = json!({
@ -147,11 +157,15 @@ impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
fn set_gas(&mut self, _gas: U256) {}
fn clone_sink(&self) -> Self::Sink {
(self.trace_sink.clone(), self.out_sink.clone())
(
self.trace_sink.clone(),
self.out_sink.clone(),
self.config.clone(),
)
}
fn finish(
result: vm::RunResult<<Self as trace::VMTracer>::Output>,
(ref mut trace_sink, ref mut out_sink): &mut Self::Sink,
(ref mut trace_sink, ref mut out_sink, _): &mut Self::Sink,
) {
match result {
Ok(success) => {
@ -240,7 +254,11 @@ impl<Trace: Writer, Out: Writer> trace::VMTracer for Informant<Trace, Out> {
fn prepare_subtrace(&mut self, code: &[u8]) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
let mut vm = Informant::new(informant.trace_sink.clone(), informant.out_sink.clone());
let mut vm = Informant::new(
informant.trace_sink.clone(),
informant.out_sink.clone(),
informant.config,
);
vm.depth = informant.depth + 1;
vm.code = code.to_vec();
informant.subinfos.push(vm);
@ -293,7 +311,10 @@ pub mod tests {
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)
(
Informant::new(trace_writer, out_writer, Config::default()),
res,
)
}
#[test]

View File

@ -141,7 +141,7 @@ pub fn run_transaction<T: Informant>(
let result = run(
&spec,
trie_spec,
transaction.gas,
transaction.tx().gas,
pre_state,
|mut client| {
let result = client.transact(env_info, transaction, trace::NoopTracer, informant);

View File

@ -57,10 +57,10 @@ use info::Informant;
const USAGE: &'static str = r#"
EVM implementation for Parity.
Copyright 2015-2019 Parity Technologies (UK) Ltd.
Copyright 2015-2020 Parity Technologies (UK) Ltd.
Usage:
openethereum-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only]
openethereum-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only --omit-storage-output --omit-memory-output]
openethereum-evm stats [options]
openethereum-evm stats-jsontests-vm <file>
openethereum-evm [options]
@ -84,16 +84,19 @@ 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).
HomesteadToDaoAt5, HomesteadToEIP150At5, Berlin, Yolo3).
--only NAME Runs only a single test matching the name.
General options:
--json Display verbose results in JSON.
--std-json Display results in standardized JSON format.
--std-err-only With --std-json redirect to err output only.
--std-out-only With --std-json redirect to out output only.
--std-dump-json Display results in standardized JSON format
with additional state dump.
--json Display verbose results in JSON.
--std-json Display results in standardized JSON format.
--std-err-only With --std-json redirect to err output only.
--std-out-only With --std-json redirect to out output only.
--omit-storage-output With --std-json omit storage output.
--omit-memory-output With --std-json omit memory output.
--std-dump-json Display results in standardized JSON format
with additional state dump.
Display result state dump in standardized JSON format.
--chain CHAIN Chain spec file path.
-h, --help Display this message and exit.
@ -107,22 +110,24 @@ fn main() {
.and_then(|d| d.deserialize())
.unwrap_or_else(|e| e.exit());
let config = args.config();
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())
run_call(args, display::json::Informant::new(config))
} 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())
run_call(args, display::std_json::Informant::err_only(config))
} else if args.flag_std_out_only {
run_call(args, display::std_json::Informant::out_only())
run_call(args, display::std_json::Informant::out_only(config))
} else {
run_call(args, display::std_json::Informant::default())
run_call(args, display::std_json::Informant::new_default(config))
};
} else {
run_call(args, display::simple::Informant::default())
run_call(args, display::simple::Informant::new(config))
}
}
@ -165,7 +170,7 @@ fn run_stats_jsontests_vm(args: Args) {
fn run_state_test(args: Args) {
use ethjson::state::test::Test;
let config = args.config();
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)),
@ -197,7 +202,6 @@ fn run_state_test(args: Args) {
{
continue;
}
for (idx, state) in states.into_iter().enumerate() {
let post_root = state.hash.into();
let transaction = multitransaction.select(&state.indexes).into();
@ -216,7 +220,7 @@ fn run_state_test(args: Args) {
post_root,
&env_info,
transaction,
display::json::Informant::default(),
display::json::Informant::new(config),
trie_spec,
)
} else if args.flag_std_dump_json || args.flag_std_json {
@ -229,7 +233,7 @@ fn run_state_test(args: Args) {
post_root,
&env_info,
transaction,
display::std_json::Informant::err_only(),
display::std_json::Informant::err_only(config),
trie_spec,
)
} else if args.flag_std_out_only {
@ -241,7 +245,7 @@ fn run_state_test(args: Args) {
post_root,
&env_info,
transaction,
display::std_json::Informant::out_only(),
display::std_json::Informant::out_only(config),
trie_spec,
)
} else {
@ -253,7 +257,7 @@ fn run_state_test(args: Args) {
post_root,
&env_info,
transaction,
display::std_json::Informant::default(),
display::std_json::Informant::new_default(config),
trie_spec,
)
}
@ -266,7 +270,7 @@ fn run_state_test(args: Args) {
post_root,
&env_info,
transaction,
display::simple::Informant::default(),
display::simple::Informant::new(config),
trie_spec,
)
}
@ -287,8 +291,16 @@ fn run_call<T: Informant>(args: Args, informant: T) {
if code.is_none() && to == Address::default() {
die("Either --code or --to is required.");
}
let mut params = ActionParams::default();
if spec.engine.params().eip2929_transition == 0 {
params.access_list.enable();
params.access_list.insert_address(from);
params.access_list.insert_address(to);
for (builtin, _) in spec.engine.builtins() {
params.access_list.insert_address(*builtin);
}
}
params.call_type = if code.is_none() {
CallType::Call
} else {
@ -331,6 +343,8 @@ struct Args {
flag_std_dump_json: bool,
flag_std_err_only: bool,
flag_std_out_only: bool,
flag_omit_storage_output: bool,
flag_omit_memory_output: bool,
}
impl Args {
@ -378,13 +392,24 @@ impl Args {
pub fn spec(&self) -> Result<spec::Spec, String> {
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)?
Some(ref spec_name) => {
let fork_spec: Result<ethjson::spec::ForkSpec, _> =
serde_json::from_str(&format!("{:?}", spec_name));
if let Ok(fork_spec) = fork_spec {
ethcore::client::EvmTestClient::spec_from_json(&fork_spec)
.expect("this forkspec is not defined")
} else {
let file = fs::File::open(spec_name).map_err(|e| format!("{}", e))?;
spec::Spec::load(&::std::env::temp_dir(), file)?
}
}
None => ethcore::ethereum::new_foundation(&::std::env::temp_dir()),
})
}
pub fn config(&self) -> display::config::Config {
display::config::Config::new(self.flag_omit_storage_output, self.flag_omit_memory_output)
}
}
fn arg<T>(v: Result<T, String>, param: &str) -> T {

Some files were not shown because too many files have changed in this diff Show More