Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bd7db96fe | ||
|
|
ff398fe7ff | ||
|
|
3ebc769757 | ||
|
|
d60e6384d7 | ||
|
|
3fd58bdcbd | ||
|
|
ecbafb2390 | ||
|
|
adabd8198c | ||
|
|
c2487cfe07 | ||
|
|
e0141f8324 | ||
|
|
b52ac20660 | ||
|
|
3c85f29f11 |
@@ -1,27 +1,3 @@
|
||||
# NOTE: if you make changes here, remember to also update:
|
||||
# scripts/test-linux.sh
|
||||
# scripts/build-linux.sh
|
||||
# scripts/build-windows.sh
|
||||
|
||||
# Using 'cfg` is broken, see https://github.com/rust-lang/cargo/issues/6858
|
||||
#[target.'cfg(target_arch = "x86_64")']
|
||||
#rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
# …so instead we list all target triples (Tier 1 64-bit platforms)
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
[target.x86_64-pc-windows-gnu]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
# Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3", "-Ctarget-feature=+crt-static"]
|
||||
|
||||
[target.x86_64-apple-darwin]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
|
||||
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,14 +1,10 @@
|
||||
_Before filing a new issue, please **provide the following information**._
|
||||
|
||||
_If you think that your issue is an exploitable security vulnerability, please mail your bugreport to security@parity.io instead; your submission might be eligible for our Bug Bounty._
|
||||
_You can find mode info on the reporting process in [SECURITY.md](https://github.com/paritytech/parity-ethereum/blob/master/SECURITY.md)_
|
||||
|
||||
|
||||
- **Parity Ethereum version**: 0.0.0
|
||||
- **Operating system**: Windows / MacOS / Linux
|
||||
- **Installation**: homebrew / one-line installer / built from source
|
||||
- **Fully synchronized**: no / yes
|
||||
- **Network**: ethereum / ropsten / goerli / ...
|
||||
- **Network**: ethereum / ropsten / kovan / ...
|
||||
- **Restarted**: no / yes
|
||||
|
||||
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
|
||||
|
||||
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,21 +0,0 @@
|
||||
Thank you for your Pull Request!
|
||||
|
||||
Before you submitting, please check that:
|
||||
|
||||
- [ ] You added a brief description of the PR, e.g.:
|
||||
- What does it do?
|
||||
- What important points reviewers should know?
|
||||
- Is there something left for follow-up PRs?
|
||||
- [ ] You labeled the PR with appropriate labels if you have permissions to do so.
|
||||
- [ ] You mentioned a related issue if this PR related to it, e.g. `Fixes #228` or `Related #1337`.
|
||||
- [ ] You asked any particular reviewers to review. If you aren't sure, start with GH suggestions.
|
||||
- [ ] Your PR adheres [the style guide](https://wiki.parity.io/Coding-guide)
|
||||
- In particular, mind the maximal line length.
|
||||
- There is no commented code checked in unless necessary.
|
||||
- Any panickers have a proof or removed.
|
||||
- [ ] You updated any rustdocs which may have changed
|
||||
|
||||
After you've read this notice feel free to remove it.
|
||||
Thank you!
|
||||
|
||||
✄ -----------------------------------------------------------------------------
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -43,6 +43,3 @@ parity-clib-examples/cpp/build/
|
||||
.vscode
|
||||
rls/
|
||||
/parity.*
|
||||
|
||||
# cargo remote artifacts
|
||||
remote-target
|
||||
|
||||
151
.gitlab-ci.yml
151
.gitlab-ci.yml
@@ -4,15 +4,19 @@ stages:
|
||||
- publish
|
||||
- optional
|
||||
|
||||
image: ${REGISTRY}/parity-ci-linux:latest
|
||||
|
||||
image: parity/parity-ci-linux:latest
|
||||
variables:
|
||||
GIT_STRATEGY: fetch
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
CARGO_HOME: "/ci-cache/${CI_PROJECT_NAME}/cargo/${CI_JOB_NAME}"
|
||||
SCCACHE_DIR: "/ci-cache/${CI_PROJECT_NAME}/sccache"
|
||||
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||
REGISTRY: registry.parity.io/parity/infrastructure/scripts
|
||||
|
||||
.no_git: &no_git # disable git strategy
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
GIT_SUBMODULE_STRATEGY: none
|
||||
|
||||
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
|
||||
only: &releaseable_branches
|
||||
@@ -37,12 +41,22 @@ variables:
|
||||
before_script:
|
||||
- rustup show
|
||||
- cargo --version
|
||||
retry:
|
||||
max: 2
|
||||
when:
|
||||
- runner_system_failure
|
||||
- unknown_failure
|
||||
- api_failure
|
||||
- SCCACHE_ERROR_LOG=/builds/parity/parity-ethereum/sccache_debug.log
|
||||
RUST_LOG=sccache=debug
|
||||
sccache --start-server
|
||||
- sccache -s
|
||||
after_script:
|
||||
# sccache debug info
|
||||
- if test -e sccache_debug.log;
|
||||
then
|
||||
echo "_____All crate-types:_____";
|
||||
grep 'parse_arguments.*--crate-type' sccache_debug.log | sed -re 's/.*"--crate-type", "([^"]+)".*/\1/' | sort | uniq -c;
|
||||
echo "_____Non-cacheable reasons:_____";
|
||||
grep CannotCache sccache_debug.log | sed -re 's/.*CannotCache\((.+)\).*/\1/' | sort | uniq -c;
|
||||
else
|
||||
echo "_____No logs from sccache_____";
|
||||
exit 0;
|
||||
fi
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
@@ -52,6 +66,7 @@ variables:
|
||||
<<: *collect_artifacts
|
||||
script:
|
||||
- scripts/gitlab/build-linux.sh
|
||||
- sccache -s
|
||||
after_script:
|
||||
- mkdir -p tools
|
||||
- cp -r scripts/docker/hub/* ./tools
|
||||
@@ -69,38 +84,21 @@ cargo-check 0 3:
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --target $CARGO_TARGET --locked --no-default-features --verbose --color=always
|
||||
- sccache --show-stats
|
||||
- sccache -s
|
||||
|
||||
cargo-check 1 3:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features --verbose --color=always
|
||||
- sccache --show-stats
|
||||
- sccache -s
|
||||
|
||||
cargo-check 2 3:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-check-evmbin:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check -p evmbin --target $CARGO_TARGET --locked --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-check-benches:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time (
|
||||
cargo check --all --benches --exclude ethash --target $CARGO_TARGET --locked --verbose --color=always;
|
||||
(cd ethash; time cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always)
|
||||
)
|
||||
- sccache --show-stats
|
||||
- sccache -s
|
||||
|
||||
cargo-audit:
|
||||
stage: test
|
||||
@@ -114,18 +112,21 @@ validate-chainspecs:
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/validate-chainspecs.sh
|
||||
- sccache -s
|
||||
|
||||
test-cpp:
|
||||
stage: build
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-cpp.sh
|
||||
- sccache -s
|
||||
|
||||
test-linux:
|
||||
stage: build
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-linux.sh stable
|
||||
- sccache -s
|
||||
|
||||
test-linux-beta:
|
||||
stage: build
|
||||
@@ -133,6 +134,7 @@ test-linux-beta:
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-linux.sh beta
|
||||
- sccache -s
|
||||
|
||||
test-linux-nightly:
|
||||
stage: build
|
||||
@@ -140,36 +142,37 @@ test-linux-nightly:
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-linux.sh nightly
|
||||
- sccache -s
|
||||
allow_failure: true
|
||||
|
||||
build-android:
|
||||
<<: *build-on-linux
|
||||
image: ${REGISTRY}/parity-ci-android:stretch
|
||||
image: parity/parity-ci-android:stretch
|
||||
variables:
|
||||
CARGO_TARGET: armv7-linux-androideabi
|
||||
|
||||
build-linux:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
# only: *releaseable_branches
|
||||
|
||||
build-linux-i386:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
image: ${REGISTRY}/parity-ci-i386:latest
|
||||
image: parity/parity-ci-i386:latest
|
||||
variables:
|
||||
CARGO_TARGET: i686-unknown-linux-gnu
|
||||
|
||||
build-linux-arm64:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
image: ${REGISTRY}/parity-ci-arm64:latest
|
||||
image: parity/parity-ci-arm64:latest
|
||||
variables:
|
||||
CARGO_TARGET: aarch64-unknown-linux-gnu
|
||||
|
||||
build-linux-armhf:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
image: ${REGISTRY}/parity-ci-armhf:latest
|
||||
image: parity/parity-ci-armhf:latest
|
||||
variables:
|
||||
CARGO_TARGET: armv7-unknown-linux-gnueabihf
|
||||
|
||||
@@ -202,10 +205,11 @@ build-windows:
|
||||
|
||||
publish-docker:
|
||||
stage: publish
|
||||
<<: *no_git
|
||||
only: *releaseable_branches
|
||||
except:
|
||||
- nightly
|
||||
when: manual
|
||||
variables:
|
||||
- $SCHEDULE_TAG == "nightly"
|
||||
dependencies:
|
||||
- build-linux
|
||||
environment:
|
||||
@@ -215,24 +219,23 @@ publish-docker:
|
||||
services:
|
||||
- docker:dind
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
DOCKER_HOST: tcp://localhost:2375
|
||||
DOCKER_DRIVER: overlay2
|
||||
GIT_STRATEGY: none
|
||||
# DOCKERFILE: tools/Dockerfile
|
||||
# CONTAINER_IMAGE: parity/parity
|
||||
# CONTAINER_IMAGE: parity/parity
|
||||
script:
|
||||
# we stopped pushing nightlies to dockerhub, will push to own registry prb.
|
||||
- ./tools/publish-docker.sh
|
||||
tags:
|
||||
- kubernetes-parity-build
|
||||
|
||||
publish-snap-nightly: &publish-snap
|
||||
publish-snap: &publish-snap
|
||||
stage: publish
|
||||
only:
|
||||
- nightly
|
||||
<<: *no_git
|
||||
only: *releaseable_branches
|
||||
image: snapcore/snapcraft
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
BUILD_ARCH: amd64
|
||||
cache: {}
|
||||
dependencies:
|
||||
@@ -242,12 +245,7 @@ publish-snap-nightly: &publish-snap
|
||||
script:
|
||||
- ./tools/publish-snap.sh
|
||||
|
||||
publish-snap-manually:
|
||||
<<: *publish-snap
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-snap-i386-nightly: &publish-snap-i386
|
||||
publish-snap-i386:
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: i386
|
||||
@@ -255,12 +253,7 @@ publish-snap-i386-nightly: &publish-snap-i386
|
||||
dependencies:
|
||||
- build-linux-i386
|
||||
|
||||
publish-snap-i386-manually:
|
||||
<<: *publish-snap-i386
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-snap-arm64-nightly: &publish-snap-arm64
|
||||
publish-snap-arm64:
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: arm64
|
||||
@@ -268,12 +261,7 @@ publish-snap-arm64-nightly: &publish-snap-arm64
|
||||
dependencies:
|
||||
- build-linux-arm64
|
||||
|
||||
publish-snap-arm64-manually:
|
||||
<<: *publish-snap-arm64
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-snap-armhf-nightly: &publish-snap-armhf
|
||||
publish-snap-armhf:
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: armhf
|
||||
@@ -281,18 +269,11 @@ publish-snap-armhf-nightly: &publish-snap-armhf
|
||||
dependencies:
|
||||
- build-linux-armhf
|
||||
|
||||
publish-snap-armhf-manually:
|
||||
<<: *publish-snap-armhf
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-onchain-nightly: &publish-onchain
|
||||
publish-onchain:
|
||||
stage: publish
|
||||
only:
|
||||
- nightly
|
||||
<<: *no_git
|
||||
only: *releaseable_branches
|
||||
cache: {}
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
dependencies:
|
||||
- build-linux
|
||||
- build-darwin
|
||||
@@ -302,18 +283,11 @@ publish-onchain-nightly: &publish-onchain
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
publish-onchain-manually:
|
||||
<<: *publish-onchain
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-release-awss3-nightly: &publish-release-awss3
|
||||
image: ${REGISTRY}/awscli:latest
|
||||
publish-awss3-release:
|
||||
image: parity/awscli:latest
|
||||
stage: publish
|
||||
only:
|
||||
- nightly
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
only: *releaseable_branches
|
||||
<<: *no_git
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
@@ -335,19 +309,13 @@ publish-release-awss3-nightly: &publish-release-awss3
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
publish-release-awss3-manually:
|
||||
<<: *publish-release-awss3
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-docs:
|
||||
stage: publish
|
||||
image: ${REGISTRY}/parity-ci-docs:latest
|
||||
image: parity/parity-ci-docs:latest
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- nightly
|
||||
when: manual
|
||||
cache: {}
|
||||
dependencies: []
|
||||
script:
|
||||
@@ -358,12 +326,11 @@ publish-docs:
|
||||
|
||||
publish-av-whitelist:
|
||||
stage: publish
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
<<: *no_git
|
||||
only: *releaseable_branches
|
||||
except:
|
||||
- nightly
|
||||
when: manual
|
||||
variables:
|
||||
- $SCHEDULE_TAG == "nightly"
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-windows
|
||||
|
||||
344
CHANGELOG.md
344
CHANGELOG.md
@@ -1,307 +1,95 @@
|
||||
## Parity-Ethereum [v2.6.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.8)
|
||||
## Parity-Ethereum [v2.5.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.5)
|
||||
|
||||
Parity Ethereum v2.6.8-beta is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall.
|
||||
Parity-Ethereum v2.5.6-stable is a bugfix release that improves stability.
|
||||
|
||||
The full list of included changes:
|
||||
* Make sure to not mark block header hash as invalid if only the body is wrong (#11356)
|
||||
|
||||
## Parity-Ethereum [v2.6.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.7)
|
||||
|
||||
Parity Ethereum v2.6.7-beta is a patch release that adds Istanbul hardfork
|
||||
block numbers for POA and xDai networks, implements ECIP-1056 and implements
|
||||
EIP-2384/2387 - Muir Glacier.
|
||||
|
||||
The full list of included changes:
|
||||
* Enable EIP-2384 for ice age hard fork (#11281)
|
||||
* ethcore/res: activate agharta on classic 9573000 (#11331)
|
||||
* Istanbul HF in xDai (2019-12-12) (#11299)
|
||||
* Istanbul HF in POA Core (2019-12-19) (#11298)
|
||||
* Istanbul HF in POA Sokol (2019-12-05) (#11282)
|
||||
* Activate ecip-1061 on kotti and mordor (#11338)
|
||||
* Enable basic verification of local transactions (#11332)
|
||||
* Disallow EIP-86 style null signatures for transactions outside tests (#11335)
|
||||
* SecretStore database migration to v4 (#11322)
|
||||
|
||||
## Parity-Ethereum [v2.6.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.6)
|
||||
|
||||
Parity Ethereum v2.6.6-beta is an emergency patch release that adds the missing
|
||||
eip1344_transition for mainnet - Users are advised to update as soon as possible
|
||||
to prevent any issues with the imminent Istanbul hardfork
|
||||
|
||||
The full list of included changes:
|
||||
* [chainspec]: add `eip1344_transition` for istanbul (#11301)
|
||||
|
||||
## Parity-Ethereum [v2.6.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.5)
|
||||
|
||||
Parity Ethereum v2.6.5-beta is a patch release that adds block numbers for activating the Istanbul hardfork on mainnet, as well as a large number of various bugfixes, QoL changes, some code cleanup/refactoring and other miscellaneous changes.
|
||||
|
||||
This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`.
|
||||
|
||||
This release includes important changes to how snapshots are produced. The size of the Ethereum account state means that producing a snapshot takes a long while; most nodes today are not able to finish before the relevant state is pruned. Starting with v2.6.5, pruning is paused while a snapshot is underway, hopefully fixing the current dearth of recent snapshots. The downside to this is that memory usage goes up while a snapshot is produced.
|
||||
* Allow specifying hostnames for node URLs
|
||||
* Fix a bug where archive nodes were losing peers
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* [CI] check evmbin build (#11096)
|
||||
* Correct EIP-712 encoding (#11092)
|
||||
* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086)
|
||||
* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053)
|
||||
* Add cargo-remote dir to .gitignore (?)
|
||||
* Update light client headers: ropsten 6631425 foundation 8798209 (#11201)
|
||||
* Update list of bootnodes for xDai chain (#11236)
|
||||
* ethcore/res: add mordor testnet configuration (#11200)
|
||||
* [chain specs]: activate Istanbul on mainnet (#11228)
|
||||
* [builtin]: support multiple prices and activations in chain spec (#11039)
|
||||
* [receipt]: add sender & receiver to RichReceipts (#11179)
|
||||
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
|
||||
* Made ecrecover implementation trait public (#11188)
|
||||
* Fix docker centos build (#11226)
|
||||
* Update MIX bootnodes. (#11203)
|
||||
* Insert explicit warning into the panic hook (#11225)
|
||||
* Use provided usd-per-eth value if an endpoint is specified (#11209)
|
||||
* Cleanup stratum a bit (#11161)
|
||||
* Add Constantinople EIPs to the dev (instant_seal) config (#10809) (already backported)
|
||||
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
|
||||
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
|
||||
* Type annotation for next_key() matching of json filter options (#11192)
|
||||
* Upgrade jsonrpc to latest (#11206)
|
||||
* [dependencies]: jsonrpc 14.0.1 (#11183)
|
||||
* Upgrade to jsonrpc v14 (#11151)
|
||||
* Switching sccache from local to Redis (#10971)
|
||||
* Snapshot restoration overhaul (#11219)
|
||||
* Add new line after writing block to hex file. (#10984)
|
||||
* Pause pruning while snapshotting (#11178)
|
||||
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
|
||||
* Fix block detail updating (#11015)
|
||||
* Make InstantSeal Instant again #11186
|
||||
* Filter out some bad ropsten warp snapshots (#11247)
|
||||
* Allow default block parameter to be blockHash (#10932)
|
||||
* Kaspersky AV whitelisting (#10919)
|
||||
* Avast whitelist script (#10900)
|
||||
* Docker images renaming (#10863)
|
||||
* Remove excessive warning (#10831)
|
||||
* Allow --nat extip:your.host.here.org (#10830)
|
||||
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
|
||||
* added new ropsten-bootnode and removed old one (#10794)
|
||||
* ethkey no longer uses byteorder (#10786)
|
||||
* Do not drop the peer with None difficulty (#10772)
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
|
||||
|
||||
## Parity-Ethereum [v2.6.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.4)
|
||||
## Parity-Ethereum [v2.5.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.5)
|
||||
|
||||
Parity Ethereum v2.6.4-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan.
|
||||
Parity-Ethereum v2.5.5-stable is a minor release that improves performance and stability.
|
||||
This release stabilises the 2.5 branch.
|
||||
|
||||
A full list of included changes:
|
||||
As of today, Parity-Ethereum 2.4 reaches end of life and everyone is
|
||||
encouraged to upgrade.
|
||||
|
||||
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)
|
||||
* cleanup json crate (#11027)
|
||||
* [json-spec] make blake2 pricing spec more readable (#11034)
|
||||
* Update JSON tests to d4f86ecf4aa7c (#11054)
|
||||
## Parity-Ethereum [v2.5.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.4)
|
||||
|
||||
## Parity-Ethereum [v2.6.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.3)
|
||||
|
||||
Parity Ethereum v2.6.3-stable is a patch release that improves security, stability and performance.
|
||||
|
||||
* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork.
|
||||
* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention.
|
||||
Parity Ethereum v2.5.4-beta is a security update that addresses servo/rust-smallvec#148
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* add more tx tests (#11038)
|
||||
* Fix parallel transactions race-condition (#10995)
|
||||
* Add blake2_f precompile (#11017)
|
||||
* [trace] introduce trace failed to Ext (#11019)
|
||||
* Edit publish-onchain.sh to use https (#11016)
|
||||
* Fix deadlock in network-devp2p (#11013)
|
||||
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
|
||||
* xDai chain support and nodes list update (#10989)
|
||||
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
|
||||
* EIP-1344 Add CHAINID op-code (#10983)
|
||||
* manual publish jobs for releases, no changes for nightlies (#10977)
|
||||
* [blooms-db] Fix benchmarks (#10974)
|
||||
* Verify transaction against its block during import (#10954)
|
||||
* Better error message for rpc gas price errors (#10931)
|
||||
* Fix fork choice (#10837)
|
||||
* Fix compilation on recent nightlies (#10991)
|
||||
* Don't build rpc with ethcore test-helpers (#11048)
|
||||
* EIP 1884 Re-pricing of trie-size dependent operations (#10992)
|
||||
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191)
|
||||
* cargo update -p smallvec ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822))
|
||||
|
||||
## Parity-Ethereum [v2.6.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.2)
|
||||
## Parity-Ethereum [v2.5.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.3)
|
||||
|
||||
Parity Ethereum v2.6.2-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post.
|
||||
Parity-Ethereum 2.5.3-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
## Parity-Ethereum [v2.6.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.1)
|
||||
|
||||
Parity-Ethereum 2.6.1-beta is a patch release that improves stability.
|
||||
|
||||
This release includes:
|
||||
* Allow specifying hostnames for node URLs
|
||||
* Fix a bug where archive nodes were losing peers
|
||||
* Add support for Energy Web Foundations new chains 'Volta' and 'EWC', and remove their deprecated 'Tobalaba' chain.
|
||||
* EthereumClassic: activate the Atlantis Hardfork
|
||||
* Clique: fix time overflow
|
||||
* State tests: treat empty accounts the same as non-existant accounts (EIP 1052)
|
||||
* Networking: support discovery-only peers (geth bootnodes)
|
||||
* Snapshotting: fix unclean shutdown while snappshotting is under way
|
||||
|
||||
The full list of included changes:
|
||||
* Add support for Energy Web Foundation's new chains (#10957)
|
||||
* Kaspersky AV whitelisting (#10919)
|
||||
* Avast whitelist script (#10900)
|
||||
* Docker images renaming (#10863)
|
||||
* Remove excessive warning (#10831)
|
||||
* Allow --nat extip:your.host.here.org (#10830)
|
||||
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
|
||||
* added new ropsten-bootnode and removed old one (#10794)
|
||||
* ethkey no longer uses byteorder (#10786)
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
|
||||
|
||||
## Parity-Ethereum [v2.6.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.0)
|
||||
|
||||
Parity-Ethereum 2.6.0-beta is a minor release that stabilizes the 2.6 branch by
|
||||
marking it as a beta release.
|
||||
|
||||
This release includes:
|
||||
* Major refactoring of the codebase
|
||||
* Many bugfixes
|
||||
* Significant improvements to logging, error and warning message clarity.
|
||||
* SecretStore: remove support of old database formats (#10757)
|
||||
* This is a potentially breaking change if you have not upgraded for
|
||||
quite some time.
|
||||
|
||||
As of today, Parity-Ethereum 2.4 reaches end of life, and everyone is
|
||||
encouraged to upgrade.
|
||||
|
||||
The full list of included changes:
|
||||
* update jsonrpc to 12.0 ([#10841](https://github.com/paritytech/parity-ethereum/pull/10841))
|
||||
* Move more code into state-account ([#10840](https://github.com/paritytech/parity-ethereum/pull/10840))
|
||||
* Extract AccountDB to account-db ([#10839](https://github.com/paritytech/parity-ethereum/pull/10839))
|
||||
* Extricate PodAccount and state Account to own crates ([#10838](https://github.com/paritytech/parity-ethereum/pull/10838))
|
||||
* Fix fork choice ([#10837](https://github.com/paritytech/parity-ethereum/pull/10837))
|
||||
* tests: Relates to #10655: Test instructions for Readme ([#10835](https://github.com/paritytech/parity-ethereum/pull/10835))
|
||||
* idiomatic changes to PodState ([#10834](https://github.com/paritytech/parity-ethereum/pull/10834))
|
||||
* Break circular dependency between Client and Engine (part 1) ([#10833](https://github.com/paritytech/parity-ethereum/pull/10833))
|
||||
* Remove excessive warning ([#10831](https://github.com/paritytech/parity-ethereum/pull/10831))
|
||||
* Allow --nat extip:your.host.here.org ([#10830](https://github.com/paritytech/parity-ethereum/pull/10830))
|
||||
* ethcore does not use byteorder ([#10829](https://github.com/paritytech/parity-ethereum/pull/10829))
|
||||
* Fix typo in README.md ([#10828](https://github.com/paritytech/parity-ethereum/pull/10828))
|
||||
* Update wordlist to v1.3 ([#10823](https://github.com/paritytech/parity-ethereum/pull/10823))
|
||||
* bump `smallvec 0.6.10` to fix vulnerability ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822))
|
||||
* removed additional_params method ([#10818](https://github.com/paritytech/parity-ethereum/pull/10818))
|
||||
* Improve logging when remote peer is unknown ([#10817](https://github.com/paritytech/parity-ethereum/pull/10817))
|
||||
* replace memzero with zeroize crate ([#10816](https://github.com/paritytech/parity-ethereum/pull/10816))
|
||||
* When updating the client or when called from RPC, sleep should mean sleep ([#10814](https://github.com/paritytech/parity-ethereum/pull/10814))
|
||||
* Don't reimplement the logic from the Default impl ([#10813](https://github.com/paritytech/parity-ethereum/pull/10813))
|
||||
* refactor: whisper: Add type aliases and update rustdocs in message.rs ([#10812](https://github.com/paritytech/parity-ethereum/pull/10812))
|
||||
* test: whisper/cli `add invalid pool size test depending on processor` ([#10811](https://github.com/paritytech/parity-ethereum/pull/10811))
|
||||
* Add Constantinople EIPs to the dev (instant_seal) config ([#10809](https://github.com/paritytech/parity-ethereum/pull/10809))
|
||||
* fix spurious test failure ([#10808](https://github.com/paritytech/parity-ethereum/pull/10808))
|
||||
* revert temp changes to .gitlab-ci.yml ([#10807](https://github.com/paritytech/parity-ethereum/pull/10807))
|
||||
* removed redundant fmt::Display implementations ([#10806](https://github.com/paritytech/parity-ethereum/pull/10806))
|
||||
* removed EthEngine alias ([#10805](https://github.com/paritytech/parity-ethereum/pull/10805))
|
||||
* ethcore-bloom-journal updated to 2018 ([#10804](https://github.com/paritytech/parity-ethereum/pull/10804))
|
||||
* Fix a few typos and unused warnings. ([#10803](https://github.com/paritytech/parity-ethereum/pull/10803))
|
||||
* updated price-info to edition 2018 ([#10801](https://github.com/paritytech/parity-ethereum/pull/10801))
|
||||
* updated parity-local-store to edition 2018 ([#10800](https://github.com/paritytech/parity-ethereum/pull/10800))
|
||||
* updated project to ansi_term 0.11 ([#10799](https://github.com/paritytech/parity-ethereum/pull/10799))
|
||||
* ethcore-light uses bincode 1.1 ([#10798](https://github.com/paritytech/parity-ethereum/pull/10798))
|
||||
* ethcore-network-devp2p uses igd 0.9 ([#10797](https://github.com/paritytech/parity-ethereum/pull/10797))
|
||||
* Better logging when backfilling ancient blocks fail ([#10796](https://github.com/paritytech/parity-ethereum/pull/10796))
|
||||
* added new ropsten-bootnode and removed old one ([#10794](https://github.com/paritytech/parity-ethereum/pull/10794))
|
||||
* Removed machine abstraction from ethcore ([#10791](https://github.com/paritytech/parity-ethereum/pull/10791))
|
||||
* Removed redundant ethcore-service error type ([#10788](https://github.com/paritytech/parity-ethereum/pull/10788))
|
||||
* Cleanup unused vm dependencies ([#10787](https://github.com/paritytech/parity-ethereum/pull/10787))
|
||||
* ethkey no longer uses byteorder ([#10786](https://github.com/paritytech/parity-ethereum/pull/10786))
|
||||
* Updated blooms-db to rust 2018 and removed redundant deps ([#10785](https://github.com/paritytech/parity-ethereum/pull/10785))
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775))
|
||||
* Do not drop the peer with None difficulty ([#10772](https://github.com/paritytech/parity-ethereum/pull/10772))
|
||||
* EIP-1702: Generalized Account Versioning Scheme ([#10771](https://github.com/paritytech/parity-ethereum/pull/10771))
|
||||
* Move Engine::register_client to be before other I/O handler registration ([#10767](https://github.com/paritytech/parity-ethereum/pull/10767))
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/paritytech/parity-ethereum/pull/10766))
|
||||
* Updated Bn128PairingImpl to use optimized batch pairing ([#10765](https://github.com/paritytech/parity-ethereum/pull/10765))
|
||||
* Remove unused code ([#10762](https://github.com/paritytech/parity-ethereum/pull/10762))
|
||||
* Initialize private tx logger only if private tx functionality is enabled ([#10758](https://github.com/paritytech/parity-ethereum/pull/10758))
|
||||
* SecretStore: remove support of old database formats ([#10757](https://github.com/paritytech/parity-ethereum/pull/10757))
|
||||
* Enable aesni ([#10756](https://github.com/paritytech/parity-ethereum/pull/10756))
|
||||
* updater: fix static id hashes initialization ([#10755](https://github.com/paritytech/parity-ethereum/pull/10755))
|
||||
* Use fewer threads for snapshotting ([#10752](https://github.com/paritytech/parity-ethereum/pull/10752))
|
||||
* Die error_chain, die ([#10747](https://github.com/paritytech/parity-ethereum/pull/10747))
|
||||
* Fix deprectation warnings on nightly ([#10746](https://github.com/paritytech/parity-ethereum/pull/10746))
|
||||
* Improve logging and cleanup in miner around block sealing ([#10745](https://github.com/paritytech/parity-ethereum/pull/10745))
|
||||
* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744))
|
||||
* fix docker tags for publishing ([#10741](https://github.com/paritytech/parity-ethereum/pull/10741))
|
||||
* refactor: Fix indentation in ethjson ([#10740](https://github.com/paritytech/parity-ethereum/pull/10740))
|
||||
* Log validator set changes in EpochManager ([#10734](https://github.com/paritytech/parity-ethereum/pull/10734))
|
||||
* Print warnings when using dangerous settings for ValidatorSet ([#10733](https://github.com/paritytech/parity-ethereum/pull/10733))
|
||||
* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731))
|
||||
* Stop breaking out of loop if a non-canonical hash is found ([#10729](https://github.com/paritytech/parity-ethereum/pull/10729))
|
||||
* Removed secret_store folder ([#10722](https://github.com/paritytech/parity-ethereum/pull/10722))
|
||||
* Revert "enable lto for release builds (#10717)" ([#10721](https://github.com/paritytech/parity-ethereum/pull/10721))
|
||||
* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720))
|
||||
* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719))
|
||||
* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717))
|
||||
* [devp2p] Update to 2018 edition ([#10716](https://github.com/paritytech/parity-ethereum/pull/10716))
|
||||
* [devp2p] Don't use `rust-crypto` ([#10714](https://github.com/paritytech/parity-ethereum/pull/10714))
|
||||
* [devp2p] Fix warnings and re-org imports ([#10710](https://github.com/paritytech/parity-ethereum/pull/10710))
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775))
|
||||
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/paritytech/parity-ethereum/pull/10705))
|
||||
* introduce MissingParent Error, fixes #10699 ([#10700](https://github.com/paritytech/parity-ethereum/pull/10700))
|
||||
* Refactor Clique stepping ([#10691](https://github.com/paritytech/parity-ethereum/pull/10691))
|
||||
* add_sync_notifier in EthPubSubClient holds on to a Client for too long ([#10689](https://github.com/paritytech/parity-ethereum/pull/10689))
|
||||
* Fix compiler warning (that will become an error) ([#10683](https://github.com/paritytech/parity-ethereum/pull/10683))
|
||||
* Don't panic if extra_data is longer than VANITY_LENGTH ([#10682](https://github.com/paritytech/parity-ethereum/pull/10682))
|
||||
* Remove annoying compiler warnings ([#10679](https://github.com/paritytech/parity-ethereum/pull/10679))
|
||||
* Remove support for hardware wallets ([#10678](https://github.com/paritytech/parity-ethereum/pull/10678))
|
||||
* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744))
|
||||
|
||||
## Parity-Ethereum [v2.5.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.2)
|
||||
|
||||
Parity-Ethereum 2.5.2-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* [CI] allow cargo audit to fail ([#10676](https://github.com/paritytech/parity-ethereum/pull/10676))
|
||||
* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673))
|
||||
* Upgrade ethereum types ([#10670](https://github.com/paritytech/parity-ethereum/pull/10670))
|
||||
* Reset blockchain properly ([#10669](https://github.com/paritytech/parity-ethereum/pull/10669))
|
||||
* fix: Move PR template into .github/ folder ([#10663](https://github.com/paritytech/parity-ethereum/pull/10663))
|
||||
* docs: evmbin - Update Rust docs ([#10658](https://github.com/paritytech/parity-ethereum/pull/10658))
|
||||
* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs ([#10657](https://github.com/paritytech/parity-ethereum/pull/10657))
|
||||
* docs: Add PR template ([#10654](https://github.com/paritytech/parity-ethereum/pull/10654))
|
||||
* docs: Add ProgPoW Rust docs to ethash module ([#10653](https://github.com/paritytech/parity-ethereum/pull/10653))
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions ([#10652](https://github.com/paritytech/parity-ethereum/pull/10652))
|
||||
* Upgrade to parity-crypto 0.4 ([#10650](https://github.com/paritytech/parity-ethereum/pull/10650))
|
||||
* fix(compilation warnings) ([#10649](https://github.com/paritytech/parity-ethereum/pull/10649))
|
||||
* [whisper] Move needed aes_gcm crypto in-crate ([#10647](https://github.com/paritytech/parity-ethereum/pull/10647))
|
||||
* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673))
|
||||
* Update publishing ([#10644](https://github.com/paritytech/parity-ethereum/pull/10644))
|
||||
* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717))
|
||||
* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719))
|
||||
* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731))
|
||||
|
||||
## Parity-Ethereum [v2.5.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.1)
|
||||
|
||||
Parity-Ethereum 2.5.1-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ci: publish docs debug ([#10638](https://github.com/paritytech/parity-ethereum/pull/10638))
|
||||
* Fix publish docs ([#10635](https://github.com/paritytech/parity-ethereum/pull/10635))
|
||||
* Fix rinkeby petersburg fork ([#10632](https://github.com/paritytech/parity-ethereum/pull/10632))
|
||||
* Update kovan.json to switch Kovan validator set to POA Consensus Contracts ([#10628](https://github.com/paritytech/parity-ethereum/pull/10628))
|
||||
* [ethcore] remove error_chain ([#10616](https://github.com/paritytech/parity-ethereum/pull/10616))
|
||||
* Remove unused import ([#10615](https://github.com/paritytech/parity-ethereum/pull/10615))
|
||||
* Adds parity_getRawBlockByNumber, parity_submitRawBlock ([#10609](https://github.com/paritytech/parity-ethereum/pull/10609))
|
||||
* adds rpc error message for --no-ancient-blocks ([#10608](https://github.com/paritytech/parity-ethereum/pull/10608))
|
||||
* Constantinople HF on POA Core ([#10606](https://github.com/paritytech/parity-ethereum/pull/10606))
|
||||
* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length ([#10605](https://github.com/paritytech/parity-ethereum/pull/10605))
|
||||
* evm: add some mulmod benches ([#10600](https://github.com/paritytech/parity-ethereum/pull/10600))
|
||||
* sccache logs to stdout ([#10596](https://github.com/paritytech/parity-ethereum/pull/10596))
|
||||
* update bootnodes ([#10595](https://github.com/paritytech/parity-ethereum/pull/10595))
|
||||
* Merge `Notifier` and `TransactionsPoolNotifier` ([#10591](https://github.com/paritytech/parity-ethereum/pull/10591))
|
||||
* fix(whisper): change expiry `unix_time + ttl + work` ([#10587](https://github.com/paritytech/parity-ethereum/pull/10587))
|
||||
* fix(evmbin): make benches compile again ([#10586](https://github.com/paritytech/parity-ethereum/pull/10586))
|
||||
* fix issue with compilation when 'slow-blocks' feature enabled ([#10585](https://github.com/paritytech/parity-ethereum/pull/10585))
|
||||
* Allow CORS requests in Secret Store API ([#10584](https://github.com/paritytech/parity-ethereum/pull/10584))
|
||||
* CI improvements ([#10579](https://github.com/paritytech/parity-ethereum/pull/10579))
|
||||
* ethcore: improve timestamp handling ([#10574](https://github.com/paritytech/parity-ethereum/pull/10574))
|
||||
* Update Issue Template to direct security issue to email ([#10562](https://github.com/paritytech/parity-ethereum/pull/10562))
|
||||
* version: bump master to 2.6 ([#10560](https://github.com/paritytech/parity-ethereum/pull/10560))
|
||||
|
||||
## Parity-Ethereum [v2.5.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.0)
|
||||
|
||||
Parity-Ethereum 2.5.0-beta is a minor release that improves performance and stabilizes the 2.5 branch by marking it as beta release.
|
||||
|
||||
- This release adds support for the Clique consensus engine ([#9981](https://github.com/paritytech/parity-ethereum/pull/9981))
|
||||
- This enables Parity-Ethereum users to use the Görli, the Kotti Classic, and the legacy Rinkeby testnet. To get started try `parity --chain goerli`; note that light client support is currently not yet fully functional.
|
||||
- This release removes the dead chain configs for Easthub and Ethereum Social ([#10531](https://github.com/paritytech/parity-ethereum/pull/10531))
|
||||
|
||||
As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/paritytech/parity-ethereum/pull/10559))
|
||||
* Watch transactions pool ([#10558](https://github.com/paritytech/parity-ethereum/pull/10558))
|
||||
* Add SealingState; don't prepare block when not ready. ([#10529](https://github.com/paritytech/parity-ethereum/pull/10529))
|
||||
* Explicitly enable or disable Stratum in config file (Issue 9785) ([#10521](https://github.com/paritytech/parity-ethereum/pull/10521))
|
||||
* Add filtering capability to `parity_pendingTransactions` (issue 8269) ([#10506](https://github.com/paritytech/parity-ethereum/pull/10506))
|
||||
* Remove calls to heapsize ([#10432](https://github.com/paritytech/parity-ethereum/pull/10432))
|
||||
* RPC: Implements eth_subscribe("syncing") ([#10311](https://github.com/paritytech/parity-ethereum/pull/10311))
|
||||
* SecretStore: non-blocking wait of session completion ([#10303](https://github.com/paritytech/parity-ethereum/pull/10303))
|
||||
* Node table limiting and cache for node filter ([#10288](https://github.com/paritytech/parity-ethereum/pull/10288))
|
||||
* SecretStore: expose restore_key_public in HTTP API ([#10241](https://github.com/paritytech/parity-ethereum/pull/10241))
|
||||
* Trivial journal for private transactions ([#10056](https://github.com/paritytech/parity-ethereum/pull/10056))
|
||||
|
||||
## Previous releases
|
||||
|
||||
- [CHANGELOG-2.5](docs/CHANGELOG-2.5.md) (_stable_)
|
||||
- [CHANGELOG-2.4](docs/CHANGELOG-2.4.md) (EOL: 2019-07-08)
|
||||
- [CHANGELOG-2.3](docs/CHANGELOG-2.3.md) (EOL: 2019-04-09)
|
||||
- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (EOL: 2019-02-25)
|
||||
- [CHANGELOG-2.1](docs/CHANGELOG-2.1.md) (EOL: 2019-01-16)
|
||||
- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (EOL: 2018-11-15)
|
||||
- [CHANGELOG-1.11](docs/CHANGELOG-1.11.md) (EOL: 2018-09-19)
|
||||
- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (EOL: 2018-07-18)
|
||||
- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09)
|
||||
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22)
|
||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
||||
- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13)
|
||||
- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19)
|
||||
- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07)
|
||||
- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12)
|
||||
- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24)
|
||||
- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02)
|
||||
|
||||
2713
Cargo.lock
generated
2713
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
29
Cargo.toml
29
Cargo.toml
@@ -2,7 +2,7 @@
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity-ethereum"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "2.6.8"
|
||||
version = "2.5.7"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
@@ -18,8 +18,8 @@ num_cpus = "1.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "1.0"
|
||||
semver = "0.9"
|
||||
ansi_term = "0.11"
|
||||
parking_lot = "0.9"
|
||||
ansi_term = "0.10"
|
||||
parking_lot = "0.7"
|
||||
regex = "1.0"
|
||||
atty = "0.2.8"
|
||||
toml = "0.4"
|
||||
@@ -29,7 +29,7 @@ serde_derive = "1.0"
|
||||
futures = "0.1"
|
||||
fdlimit = "0.1"
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
jsonrpc-core = "14.0.3"
|
||||
jsonrpc-core = "10.0.1"
|
||||
parity-bytes = "0.1"
|
||||
common-types = { path = "ethcore/types" }
|
||||
ethcore = { path = "ethcore", features = ["parity"] }
|
||||
@@ -45,11 +45,11 @@ ethcore-network = { path = "util/network" }
|
||||
ethcore-private-tx = { path = "ethcore/private-tx" }
|
||||
ethcore-service = { path = "ethcore/service" }
|
||||
ethcore-sync = { path = "ethcore/sync" }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "accounts/ethkey" }
|
||||
ethstore = { path = "accounts/ethstore" }
|
||||
node-filter = { path = "ethcore/node-filter" }
|
||||
rlp = "0.4.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
cli-signer= { path = "cli-signer" }
|
||||
parity-daemonize = "0.3"
|
||||
parity-hash-fetch = { path = "updater/hash-fetch" }
|
||||
@@ -63,18 +63,16 @@ parity-whisper = { path = "whisper" }
|
||||
parity-path = "0.1"
|
||||
dir = { path = "util/dir" }
|
||||
panic_hook = { path = "util/panic-hook" }
|
||||
keccak-hash = "0.2.0"
|
||||
keccak-hash = "0.1"
|
||||
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||
kvdb = "0.1"
|
||||
kvdb-rocksdb = "0.1.5"
|
||||
kvdb-rocksdb = "0.1.3"
|
||||
journaldb = { path = "util/journaldb" }
|
||||
|
||||
ethcore-secretstore = { path = "secret-store", optional = true }
|
||||
|
||||
registrar = { path = "util/registrar" }
|
||||
|
||||
parity-util-mem = { version = "0.1", features = ["jemalloc-global"] }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
|
||||
@@ -83,6 +81,7 @@ pretty_assertions = "0.1"
|
||||
ipnetwork = "0.12.6"
|
||||
tempdir = "0.3"
|
||||
fake-fetch = { path = "util/fake-fetch" }
|
||||
lazy_static = "1.2.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
||||
@@ -92,6 +91,7 @@ default = ["accounts"]
|
||||
accounts = ["ethcore-accounts", "parity-rpc/accounts"]
|
||||
miner-debug = ["ethcore/miner-debug"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
ci-skip-tests = ["ethcore/ci-skip-tests"]
|
||||
test-heavy = ["ethcore/test-heavy"]
|
||||
evm-debug = ["ethcore/evm-debug"]
|
||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||
@@ -118,6 +118,10 @@ path = "parity/lib.rs"
|
||||
path = "parity/main.rs"
|
||||
name = "parity"
|
||||
|
||||
[profile.test]
|
||||
lto = false
|
||||
opt-level = 3 # makes tests slower to compile, but faster to run
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = true
|
||||
@@ -138,5 +142,8 @@ members = [
|
||||
"util/keccak-hasher",
|
||||
"util/patricia-trie-ethereum",
|
||||
"util/fastmap",
|
||||
"util/time-utils",
|
||||
"util/time-utils"
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
heapsize = { git = "https://github.com/cheme/heapsize.git", branch = "ec-macfix" }
|
||||
|
||||
51
README.md
51
README.md
@@ -16,12 +16,11 @@
|
||||
3.2 [Building from Source Code](#chapter-0032)<br>
|
||||
3.3 [Simple One-Line Installer for Mac and Linux](#chapter-0033)<br>
|
||||
3.4 [Starting Parity Ethereum](#chapter-0034)
|
||||
4. [Testing](#chapter-004)
|
||||
5. [Documentation](#chapter-005)
|
||||
6. [Toolchain](#chapter-006)
|
||||
7. [Community](#chapter-007)
|
||||
8. [Contributing](#chapter-008)
|
||||
9. [License](#chapter-009)
|
||||
4. [Documentation](#chapter-004)
|
||||
5. [Toolchain](#chapter-005)
|
||||
6. [Community](#chapter-006)
|
||||
7. [Contributing](#chapter-007)
|
||||
8. [License](#chapter-008)
|
||||
|
||||
|
||||
## 1. Description <a id="chapter-001"></a>
|
||||
@@ -57,7 +56,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
Parity Ethereum also requires `gcc`, `g++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
|
||||
- OSX:
|
||||
```bash
|
||||
@@ -66,7 +65,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
||||
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
|
||||
- Windows:
|
||||
- Windows
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the `rustup` installer from
|
||||
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the `msvc` toolchain:
|
||||
```bash
|
||||
@@ -149,25 +148,7 @@ To start Parity Ethereum as a regular user using `systemd` init:
|
||||
2. Copy release to bin folder, write `sudo install ./target/release/parity /usr/bin/parity`
|
||||
3. To configure Parity Ethereum, write a `/etc/parity/config.toml` config file, see [Configuring Parity Ethereum](https://paritytech.github.io/wiki/Configuring-Parity) for details.
|
||||
|
||||
## 4. Testing <a id="chapter-004"></a>
|
||||
|
||||
Download the required test files: `git submodule update --init --recursive`. You can run tests with the following commands:
|
||||
|
||||
* **All** packages
|
||||
```
|
||||
cargo test --all
|
||||
```
|
||||
|
||||
* Specific package
|
||||
```
|
||||
cargo test --package <spec>
|
||||
```
|
||||
|
||||
Replace `<spec>` with one of the packages from the [package list](#package-list) (e.g. `cargo test --package evmbin`).
|
||||
|
||||
You can show your logs in the test output by passing `--nocapture` (i.e. `cargo test --package evmbin -- --nocapture`)
|
||||
|
||||
## 5. Documentation <a id="chapter-005"></a>
|
||||
## 4. Documentation <a id="chapter-004"></a>
|
||||
|
||||
Official website: https://parity.io
|
||||
|
||||
@@ -179,20 +160,16 @@ You can generate documentation for Parity Ethereum Rust packages that automatica
|
||||
|
||||
* **All** packages
|
||||
```
|
||||
cargo doc --document-private-items --open
|
||||
cargo doc --open
|
||||
```
|
||||
|
||||
* Specific package
|
||||
```
|
||||
cargo doc --package <spec> -- --document-private-items --open
|
||||
cargo doc --package <spec> --open
|
||||
```
|
||||
|
||||
Use`--document-private-items` to also view private documentation and `--no-deps` to exclude building documentation for dependencies.
|
||||
|
||||
Replacing `<spec>` with one of the following from the details section below (i.e. `cargo doc --package parity-ethereum --open`):
|
||||
|
||||
<a id="package-list"></a>
|
||||
**Package List**
|
||||
<details><p>
|
||||
|
||||
* Parity Ethereum (EthCore) Client Application
|
||||
@@ -353,7 +330,7 @@ Example (generic documentation comment):
|
||||
///
|
||||
```
|
||||
|
||||
## 6. Toolchain <a id="chapter-006"></a>
|
||||
## 5. Toolchain <a id="chapter-005"></a>
|
||||
|
||||
In addition to the Parity Ethereum client, there are additional tools in this repository available:
|
||||
|
||||
@@ -365,7 +342,7 @@ In addition to the Parity Ethereum client, there are additional tools in this re
|
||||
The following tool is available in a separate repository:
|
||||
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum Encoding of Function Calls. [Docs here](https://crates.io/crates/ethabi)
|
||||
|
||||
## 7. Community <a id="chapter-007"></a>
|
||||
## 6. Community <a id="chapter-006"></a>
|
||||
|
||||
### Join the chat!
|
||||
|
||||
@@ -378,7 +355,7 @@ Questions? Get in touch with us on Gitter:
|
||||
Alternatively, join our community on Matrix:
|
||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||
|
||||
## 8. Contributing <a id="chapter-008"></a>
|
||||
## 7. Contributing <a id="chapter-007"></a>
|
||||
|
||||
An introduction has been provided in the ["So You Want to be a Core Developer" presentation slides by Hernando Castano](http://tiny.cc/contrib-to-parity-eth). Additional guidelines are provided in [CONTRIBUTING](./.github/CONTRIBUTING.md).
|
||||
|
||||
@@ -386,6 +363,6 @@ An introduction has been provided in the ["So You Want to be a Core Developer" p
|
||||
|
||||
[CODE_OF_CONDUCT](./.github/CODE_OF_CONDUCT.md)
|
||||
|
||||
## 9. License <a id="chapter-009"></a>
|
||||
## 8. License <a id="chapter-008"></a>
|
||||
|
||||
[LICENSE](./LICENSE)
|
||||
|
||||
@@ -8,14 +8,21 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common-types = { path = "../ethcore/types" }
|
||||
ethkey = { path = "ethkey" }
|
||||
ethstore = { path = "ethstore" }
|
||||
log = "0.4"
|
||||
parking_lot = "0.9"
|
||||
parking_lot = "0.7"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies]
|
||||
hardware-wallet = { path = "hw" }
|
||||
|
||||
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies]
|
||||
fake-hardware-wallet = { path = "fake-hardware-wallet" }
|
||||
|
||||
[dev-dependencies]
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
tempdir = "0.3"
|
||||
|
||||
@@ -6,16 +6,16 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
edit-distance = "2.0"
|
||||
parity-crypto = "0.4.0"
|
||||
parity-crypto = "0.3.0"
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
parity-wordlist = "1.3"
|
||||
memzero = { path = "../../util/memzero" }
|
||||
parity-wordlist = "1.2"
|
||||
quick-error = "1.2.2"
|
||||
rand = "0.6"
|
||||
rand = "0.4"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
tiny-keccak = "1.4"
|
||||
zeroize = "0.9.1"
|
||||
|
||||
@@ -47,7 +47,7 @@ impl Generator for BrainPrefix {
|
||||
for _ in 0..self.iterations {
|
||||
let phrase = wordlist::random_phrase(self.no_of_words);
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address().as_ref().starts_with(&self.prefix) {
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
self.last_phrase = phrase;
|
||||
return Ok(keypair)
|
||||
}
|
||||
@@ -65,6 +65,6 @@ mod tests {
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0x00u8];
|
||||
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap();
|
||||
assert!(keypair.address().as_bytes().starts_with(&prefix));
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ pub mod ecdh {
|
||||
};
|
||||
|
||||
let publ = key::PublicKey::from_slice(context, &pdata)?;
|
||||
let sec = key::SecretKey::from_slice(context, secret.as_bytes())?;
|
||||
let sec = key::SecretKey::from_slice(context, &secret)?;
|
||||
let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec);
|
||||
|
||||
Secret::from_unsafe_slice(&shared[0..32])
|
||||
@@ -89,12 +89,12 @@ pub mod ecies {
|
||||
msg[0] = 0x04u8;
|
||||
{
|
||||
let msgd = &mut msg[1..];
|
||||
msgd[0..64].copy_from_slice(r.public().as_bytes());
|
||||
msgd[0..64].copy_from_slice(r.public());
|
||||
let iv = H128::random();
|
||||
msgd[64..80].copy_from_slice(iv.as_bytes());
|
||||
msgd[64..80].copy_from_slice(&iv);
|
||||
{
|
||||
let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())];
|
||||
aes::encrypt_128_ctr(ekey, iv.as_bytes(), plain, cipher)?;
|
||||
aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?;
|
||||
}
|
||||
let mut hmac = hmac::Signer::with(&mkey);
|
||||
{
|
||||
@@ -156,7 +156,7 @@ pub mod ecies {
|
||||
let mut hasher = digest::Hasher::sha256();
|
||||
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8];
|
||||
hasher.update(&ctrs);
|
||||
hasher.update(secret.as_bytes());
|
||||
hasher.update(secret);
|
||||
hasher.update(s1);
|
||||
let d = hasher.finish();
|
||||
&mut dest[written..(written + 32)].copy_from_slice(&d);
|
||||
|
||||
@@ -63,7 +63,7 @@ impl Label for H256 {
|
||||
fn len() -> usize { 32 }
|
||||
|
||||
fn store(&self, target: &mut [u8]) {
|
||||
(&mut target[0..32]).copy_from_slice(self.as_bytes());
|
||||
self.copy_to(&mut target[0..32]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ impl ExtendedKeyPair {
|
||||
pub fn with_seed(seed: &[u8]) -> Result<ExtendedKeyPair, DerivationError> {
|
||||
let (master_key, chain_code) = derivation::seed_pair(seed);
|
||||
Ok(ExtendedKeyPair::with_secret(
|
||||
Secret::from_unsafe_slice(master_key.as_bytes()).map_err(|_| DerivationError::InvalidSeed)?,
|
||||
Secret::from_unsafe_slice(&*master_key).map_err(|_| DerivationError::InvalidSeed)?,
|
||||
chain_code,
|
||||
))
|
||||
}
|
||||
@@ -207,13 +207,12 @@ impl ExtendedKeyPair {
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
mod derivation {
|
||||
use parity_crypto::hmac;
|
||||
use ethereum_types::{BigEndianHash, U256, U512, H512, H256};
|
||||
use ethereum_types::{U256, U512, H512, H256};
|
||||
use secp256k1::key::{SecretKey, PublicKey};
|
||||
use SECP256K1;
|
||||
use keccak;
|
||||
use math::curve_order;
|
||||
use super::{Label, Derivation};
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -237,18 +236,18 @@ mod derivation {
|
||||
}
|
||||
|
||||
fn hmac_pair(data: &[u8], private_key: H256, chain_code: H256) -> (H256, H256) {
|
||||
let private: U256 = private_key.into_uint();
|
||||
let private: U256 = private_key.into();
|
||||
|
||||
// produces 512-bit derived hmac (I)
|
||||
let skey = hmac::SigKey::sha512(chain_code.as_bytes());
|
||||
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||
let i_512 = hmac::sign(&skey, &data[..]);
|
||||
|
||||
// left most 256 bits are later added to original private key
|
||||
let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into_uint();
|
||||
let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into();
|
||||
// right most 256 bits are new chain code for later derivations
|
||||
let next_chain_code = H256::from_slice(&i_512[32..64]);
|
||||
let next_chain_code = H256::from(&i_512[32..64]);
|
||||
|
||||
let child_key = BigEndianHash::from_uint(&private_add(hmac_key, private));
|
||||
let child_key = private_add(hmac_key, private).into();
|
||||
(child_key, next_chain_code)
|
||||
}
|
||||
|
||||
@@ -257,7 +256,7 @@ mod derivation {
|
||||
fn private_soft<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label {
|
||||
let mut data = vec![0u8; 33 + T::len()];
|
||||
|
||||
let sec_private = SecretKey::from_slice(&SECP256K1, private_key.as_bytes())
|
||||
let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key)
|
||||
.expect("Caller should provide valid private key");
|
||||
let sec_public = PublicKey::from_secret_key(&SECP256K1, &sec_private)
|
||||
.expect("Caller should provide valid private key");
|
||||
@@ -276,7 +275,7 @@ mod derivation {
|
||||
// corresponding public keys of the original and derived private keys
|
||||
fn private_hard<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label {
|
||||
let mut data: Vec<u8> = vec![0u8; 33 + T::len()];
|
||||
let private: U256 = private_key.into_uint();
|
||||
let private: U256 = private_key.into();
|
||||
|
||||
// 0x00 (padding) -- private_key -- index
|
||||
// 0 -- 1..33 -- 33..end
|
||||
@@ -293,8 +292,9 @@ mod derivation {
|
||||
|
||||
// todo: surely can be optimized
|
||||
fn modulo(u1: U512, u2: U256) -> U256 {
|
||||
let m = u1 % U512::from(u2);
|
||||
m.try_into().expect("U512 modulo U256 should fit into U256; qed")
|
||||
let dv = u1 / U512::from(u2);
|
||||
let md = u1 - (dv * U512::from(u2));
|
||||
md.into()
|
||||
}
|
||||
|
||||
pub fn public<T>(public_key: H512, chain_code: H256, derivation: Derivation<T>) -> Result<(H512, H256), Error> where T: Label {
|
||||
@@ -305,7 +305,7 @@ mod derivation {
|
||||
|
||||
let mut public_sec_raw = [0u8; 65];
|
||||
public_sec_raw[0] = 4;
|
||||
public_sec_raw[1..65].copy_from_slice(public_key.as_bytes());
|
||||
public_sec_raw[1..65].copy_from_slice(&*public_key);
|
||||
let public_sec = PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?;
|
||||
let public_serialized = public_sec.serialize_vec(&SECP256K1, true);
|
||||
|
||||
@@ -316,15 +316,15 @@ mod derivation {
|
||||
index.store(&mut data[33..(33 + T::len())]);
|
||||
|
||||
// HMAC512SHA produces [derived private(256); new chain code(256)]
|
||||
let skey = hmac::SigKey::sha512(chain_code.as_bytes());
|
||||
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||
let i_512 = hmac::sign(&skey, &data[..]);
|
||||
|
||||
let new_private = H256::from_slice(&i_512[0..32]);
|
||||
let new_chain_code = H256::from_slice(&i_512[32..64]);
|
||||
let new_private = H256::from(&i_512[0..32]);
|
||||
let new_chain_code = H256::from(&i_512[32..64]);
|
||||
|
||||
// Generated private key can (extremely rarely) be out of secp256k1 key field
|
||||
if curve_order() <= new_private.into_uint() { return Err(Error::MissingIndex); }
|
||||
let new_private_sec = SecretKey::from_slice(&SECP256K1, new_private.as_bytes())
|
||||
if curve_order() <= new_private.clone().into() { return Err(Error::MissingIndex); }
|
||||
let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private)
|
||||
.expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed");
|
||||
let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec)
|
||||
.expect("Valid private key produces valid public key");
|
||||
@@ -336,7 +336,7 @@ mod derivation {
|
||||
let serialized = new_public.serialize_vec(&SECP256K1, false);
|
||||
|
||||
Ok((
|
||||
H512::from_slice(&serialized[1..65]),
|
||||
H512::from(&serialized[1..65]),
|
||||
new_chain_code,
|
||||
))
|
||||
}
|
||||
@@ -347,18 +347,18 @@ mod derivation {
|
||||
|
||||
pub fn chain_code(secret: H256) -> H256 {
|
||||
// 10,000 rounds of sha3
|
||||
let mut running_sha3 = sha3(secret.as_bytes());
|
||||
for _ in 0..99999 { running_sha3 = sha3(running_sha3.as_bytes()); }
|
||||
let mut running_sha3 = sha3(&*secret);
|
||||
for _ in 0..99999 { running_sha3 = sha3(&*running_sha3); }
|
||||
running_sha3
|
||||
}
|
||||
|
||||
pub fn point(secret: H256) -> Result<H512, Error> {
|
||||
let sec = SecretKey::from_slice(&SECP256K1, secret.as_bytes())
|
||||
let sec = SecretKey::from_slice(&SECP256K1, &*secret)
|
||||
.map_err(|_| Error::InvalidPoint)?;
|
||||
let public_sec = PublicKey::from_secret_key(&SECP256K1, &sec)
|
||||
.map_err(|_| Error::InvalidPoint)?;
|
||||
let serialized = public_sec.serialize_vec(&SECP256K1, false);
|
||||
Ok(H512::from_slice(&serialized[1..65]))
|
||||
Ok(H512::from(&serialized[1..65]))
|
||||
}
|
||||
|
||||
pub fn seed_pair(seed: &[u8]) -> (H256, H256) {
|
||||
@@ -377,13 +377,12 @@ mod tests {
|
||||
use super::{ExtendedSecret, ExtendedPublic, ExtendedKeyPair};
|
||||
use secret::Secret;
|
||||
use std::str::FromStr;
|
||||
use ethereum_types::{H128, H256, H512};
|
||||
use ethereum_types::{H128, H256};
|
||||
use super::{derivation, Derivation};
|
||||
|
||||
fn master_chain_basic() -> (H256, H256) {
|
||||
let seed = H128::from_str("000102030405060708090a0b0c0d0e0f")
|
||||
.expect("Seed should be valid H128")
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
|
||||
derivation::seed_pair(&*seed)
|
||||
@@ -399,39 +398,27 @@ mod tests {
|
||||
#[test]
|
||||
fn smoky() {
|
||||
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap();
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::zero());
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into());
|
||||
|
||||
// hardened
|
||||
assert_eq!(&**extended_secret.as_raw(), &*secret);
|
||||
assert_eq!(
|
||||
**extended_secret.derive(2147483648.into()).as_raw(),
|
||||
H256::from_str("0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6").unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
**extended_secret.derive(2147483649.into()).as_raw(),
|
||||
H256::from_str("44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f").unwrap(),
|
||||
);
|
||||
assert_eq!(&**extended_secret.derive(2147483648.into()).as_raw(), &"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into());
|
||||
assert_eq!(&**extended_secret.derive(2147483649.into()).as_raw(), &"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into());
|
||||
|
||||
// normal
|
||||
assert_eq!(**extended_secret.derive(0.into()).as_raw(), H256::from_str("bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6").unwrap());
|
||||
assert_eq!(**extended_secret.derive(1.into()).as_raw(), H256::from_str("bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc").unwrap());
|
||||
assert_eq!(**extended_secret.derive(2.into()).as_raw(), H256::from_str("86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268").unwrap());
|
||||
assert_eq!(&**extended_secret.derive(0.into()).as_raw(), &"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into());
|
||||
assert_eq!(&**extended_secret.derive(1.into()).as_raw(), &"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into());
|
||||
assert_eq!(&**extended_secret.derive(2.into()).as_raw(), &"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into());
|
||||
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created");
|
||||
let derived_public = extended_public.derive(0.into()).expect("First derivation of public should succeed");
|
||||
assert_eq!(
|
||||
*derived_public.public(),
|
||||
H512::from_str("f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94").unwrap(),
|
||||
);
|
||||
assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into());
|
||||
|
||||
let keypair = ExtendedKeyPair::with_secret(
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(),
|
||||
H256::from_low_u64_be(64),
|
||||
);
|
||||
assert_eq!(
|
||||
**keypair.derive(2147483648u32.into()).expect("Derivation of keypair should succeed").secret().as_raw(),
|
||||
H256::from_str("edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c").unwrap(),
|
||||
064.into(),
|
||||
);
|
||||
assert_eq!(&**keypair.derive(2147483648u32.into()).expect("Derivation of keypair should succeed").secret().as_raw(), &"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -439,7 +426,7 @@ mod tests {
|
||||
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap();
|
||||
let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap();
|
||||
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::zero());
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into());
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created");
|
||||
|
||||
let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret));
|
||||
@@ -454,18 +441,15 @@ mod tests {
|
||||
fn h256_hard() {
|
||||
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap();
|
||||
let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap();
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::from_low_u64_be(1));
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into());
|
||||
|
||||
assert_eq!(
|
||||
**extended_secret.derive(Derivation::Hard(derivation_secret)).as_raw(),
|
||||
H256::from_str("2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486").unwrap(),
|
||||
);
|
||||
assert_eq!(&**extended_secret.derive(Derivation::Hard(derivation_secret)).as_raw(), &"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_() {
|
||||
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap();
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::from_low_u64_be(1));
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into());
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created");
|
||||
|
||||
let derived_secret0 = extended_secret.derive(0.into());
|
||||
@@ -480,7 +464,6 @@ mod tests {
|
||||
fn test_seeds() {
|
||||
let seed = H128::from_str("000102030405060708090a0b0c0d0e0f")
|
||||
.expect("Seed should be valid H128")
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
|
||||
// private key from bitcoin test vector
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
use std::fmt;
|
||||
use secp256k1::key;
|
||||
use rustc_hex::ToHex;
|
||||
use keccak::Keccak256;
|
||||
use super::{Secret, Public, Address, SECP256K1, Error};
|
||||
use parity_crypto::Keccak256 as _;
|
||||
|
||||
pub fn public_to_address(public: &Public) -> Address {
|
||||
let hash = public.keccak256();
|
||||
let mut result = Address::zero();
|
||||
result.as_bytes_mut().copy_from_slice(&hash[12..]);
|
||||
let mut result = Address::default();
|
||||
result.copy_from_slice(&hash[12..]);
|
||||
result
|
||||
}
|
||||
|
||||
@@ -35,9 +36,9 @@ pub struct KeyPair {
|
||||
|
||||
impl fmt::Display for KeyPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
writeln!(f, "secret: {:x}", self.secret)?;
|
||||
writeln!(f, "public: {:x}", self.public)?;
|
||||
write!(f, "address: {:x}", self.address())
|
||||
writeln!(f, "secret: {}", self.secret.to_hex())?;
|
||||
writeln!(f, "public: {}", self.public.to_hex())?;
|
||||
write!(f, "address: {}", self.address().to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +51,7 @@ impl KeyPair {
|
||||
let serialized = pub_key.serialize_vec(context, false);
|
||||
|
||||
let mut public = Public::default();
|
||||
public.as_bytes_mut().copy_from_slice(&serialized[1..65]);
|
||||
public.copy_from_slice(&serialized[1..65]);
|
||||
|
||||
let keypair = KeyPair {
|
||||
secret: secret,
|
||||
@@ -69,7 +70,7 @@ impl KeyPair {
|
||||
let serialized = publ.serialize_vec(context, false);
|
||||
let secret = Secret::from(sec);
|
||||
let mut public = Public::default();
|
||||
public.as_bytes_mut().copy_from_slice(&serialized[1..65]);
|
||||
public.copy_from_slice(&serialized[1..65]);
|
||||
|
||||
KeyPair {
|
||||
secret: secret,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
extern crate edit_distance;
|
||||
extern crate parity_crypto;
|
||||
extern crate ethereum_types;
|
||||
extern crate memzero;
|
||||
extern crate parity_wordlist;
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
@@ -27,7 +28,6 @@ extern crate rustc_hex;
|
||||
extern crate secp256k1;
|
||||
extern crate serde;
|
||||
extern crate tiny_keccak;
|
||||
extern crate zeroize;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use super::{SECP256K1, Public, Secret, Error};
|
||||
use secp256k1::key;
|
||||
use secp256k1::constants::{GENERATOR_X, GENERATOR_Y, CURVE_ORDER};
|
||||
use ethereum_types::{BigEndianHash as _, U256, H256};
|
||||
use ethereum_types::{U256, H256};
|
||||
|
||||
/// Whether the public key is valid.
|
||||
pub fn public_is_valid(public: &Public) -> bool {
|
||||
@@ -78,7 +78,7 @@ pub fn generation_point() -> Public {
|
||||
|
||||
/// Return secp256k1 elliptic curve order
|
||||
pub fn curve_order() -> U256 {
|
||||
H256::from_slice(&CURVE_ORDER).into_uint()
|
||||
H256::from_slice(&CURVE_ORDER).into()
|
||||
}
|
||||
|
||||
fn to_secp256k1_public(public: &Public) -> Result<key::PublicKey, Error> {
|
||||
@@ -93,7 +93,7 @@ fn to_secp256k1_public(public: &Public) -> Result<key::PublicKey, Error> {
|
||||
|
||||
fn set_public(public: &mut Public, key_public: &key::PublicKey) {
|
||||
let key_public_serialized = key_public.serialize_vec(&SECP256K1, false);
|
||||
public.as_bytes_mut().copy_from_slice(&key_public_serialized[1..65]);
|
||||
public.copy_from_slice(&key_public_serialized[1..65]);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Generator for Prefix {
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let keypair = Random.generate()?;
|
||||
if keypair.address().as_ref().starts_with(&self.prefix) {
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
return Ok(keypair)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,6 @@ mod tests {
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0xffu8];
|
||||
let keypair = Prefix::new(prefix.clone(), usize::max_value()).generate().unwrap();
|
||||
assert!(keypair.address().as_bytes().starts_with(&prefix));
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
use rand::os::OsRng;
|
||||
use super::{Generator, KeyPair, SECP256K1};
|
||||
|
||||
/// Randomly generates new keypair, instantiating the RNG each time.
|
||||
|
||||
@@ -21,23 +21,17 @@ use rustc_hex::ToHex;
|
||||
use secp256k1::constants::{SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE};
|
||||
use secp256k1::key;
|
||||
use ethereum_types::H256;
|
||||
use zeroize::Zeroize;
|
||||
use memzero::Memzero;
|
||||
use {Error, SECP256K1};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Secret {
|
||||
inner: H256,
|
||||
}
|
||||
|
||||
impl Drop for Secret {
|
||||
fn drop(&mut self) {
|
||||
self.inner.0.zeroize()
|
||||
}
|
||||
inner: Memzero<H256>,
|
||||
}
|
||||
|
||||
impl ToHex for Secret {
|
||||
fn to_hex(&self) -> String {
|
||||
format!("{:x}", self.inner)
|
||||
format!("{:x}", *self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,14 +59,14 @@ impl Secret {
|
||||
if key.len() != 32 {
|
||||
return None
|
||||
}
|
||||
let mut h = H256::zero();
|
||||
h.as_bytes_mut().copy_from_slice(&key[0..32]);
|
||||
Some(Secret { inner: h })
|
||||
let mut h = H256::default();
|
||||
h.copy_from_slice(&key[0..32]);
|
||||
Some(Secret { inner: Memzero::from(h) })
|
||||
}
|
||||
|
||||
/// Creates zero key, which is invalid for crypto operations, but valid for math operation.
|
||||
pub fn zero() -> Self {
|
||||
Secret { inner: H256::zero() }
|
||||
Secret { inner: Memzero::from(H256::default()) }
|
||||
}
|
||||
|
||||
/// Imports and validates the key.
|
||||
@@ -220,7 +214,7 @@ impl FromStr for Secret {
|
||||
|
||||
impl From<[u8; 32]> for Secret {
|
||||
fn from(k: [u8; 32]) -> Self {
|
||||
Secret { inner: H256(k) }
|
||||
Secret { inner: Memzero::from(H256(k)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,44 +68,24 @@ impl Signature {
|
||||
/// Create a signature object from the sig.
|
||||
pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Self {
|
||||
let mut sig = [0u8; 65];
|
||||
sig[0..32].copy_from_slice(r.as_ref());
|
||||
sig[32..64].copy_from_slice(s.as_ref());
|
||||
sig[0..32].copy_from_slice(&r);
|
||||
sig[32..64].copy_from_slice(&s);
|
||||
sig[64] = v;
|
||||
Signature(sig)
|
||||
}
|
||||
|
||||
/// Check if this is a "low" signature.
|
||||
pub fn is_low_s(&self) -> bool {
|
||||
// "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0"
|
||||
const MASK: H256 = H256([
|
||||
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D,
|
||||
0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0,
|
||||
]);
|
||||
H256::from_slice(self.s()) <= MASK
|
||||
H256::from_slice(self.s()) <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into()
|
||||
}
|
||||
|
||||
/// Check if each component of the signature is in range.
|
||||
pub fn is_valid(&self) -> bool {
|
||||
// "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
|
||||
const MASK: H256 = H256([
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
|
||||
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
|
||||
]);
|
||||
const ONE: H256 = H256([
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
]);
|
||||
let r = H256::from_slice(self.r());
|
||||
let s = H256::from_slice(self.s());
|
||||
self.v() <= 1 &&
|
||||
r < MASK && r >= ONE &&
|
||||
s < MASK && s >= ONE
|
||||
H256::from_slice(self.r()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() &&
|
||||
H256::from_slice(self.r()) >= 1.into() &&
|
||||
H256::from_slice(self.s()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() &&
|
||||
H256::from_slice(self.s()) >= 1.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +190,7 @@ impl DerefMut for Signature {
|
||||
|
||||
pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
|
||||
let context = &SECP256K1;
|
||||
let sec = SecretKey::from_slice(context, secret.as_ref())?;
|
||||
let sec = SecretKey::from_slice(context, &secret)?;
|
||||
let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?;
|
||||
let (rec_id, data) = s.serialize_compact(context);
|
||||
let mut data_arr = [0; 65];
|
||||
@@ -228,7 +208,7 @@ pub fn verify_public(public: &Public, signature: &Signature, message: &Message)
|
||||
|
||||
let pdata: [u8; 65] = {
|
||||
let mut temp = [4u8; 65];
|
||||
temp[1..65].copy_from_slice(public.as_bytes());
|
||||
temp[1..65].copy_from_slice(&**public);
|
||||
temp
|
||||
};
|
||||
|
||||
@@ -253,7 +233,7 @@ pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error
|
||||
let serialized = pubkey.serialize_vec(context, false);
|
||||
|
||||
let mut public = Public::default();
|
||||
public.as_bytes_mut().copy_from_slice(&serialized[1..65]);
|
||||
public.copy_from_slice(&serialized[1..65]);
|
||||
Ok(public)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
libc = "0.2"
|
||||
rand = "0.6"
|
||||
rand = "0.4"
|
||||
ethkey = { path = "../ethkey" }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
@@ -16,13 +16,14 @@ rustc-hex = "1.0"
|
||||
tiny-keccak = "1.4"
|
||||
time = "0.1.34"
|
||||
itertools = "0.5"
|
||||
parking_lot = "0.9"
|
||||
parity-crypto = "0.4.0"
|
||||
ethereum-types = "0.6.0"
|
||||
parking_lot = "0.7"
|
||||
parity-crypto = "0.3.0"
|
||||
ethereum-types = "0.4"
|
||||
dir = { path = "../../util/dir" }
|
||||
smallvec = "0.6"
|
||||
parity-wordlist = "1.0"
|
||||
tempdir = "0.3"
|
||||
lazy_static = "1.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
matches = "0.1"
|
||||
|
||||
@@ -11,7 +11,7 @@ num_cpus = "1.6"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parking_lot = "0.9"
|
||||
parking_lot = "0.7"
|
||||
ethstore = { path = "../" }
|
||||
dir = { path = '../../../util/dir' }
|
||||
panic_hook = { path = "../../../util/panic-hook" }
|
||||
@@ -22,4 +22,4 @@ path = "src/main.rs"
|
||||
doc = false
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
tempdir = "0.3.5"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::str;
|
||||
use std::num::NonZeroU32;
|
||||
use ethkey::{Password, Secret};
|
||||
use {json, Error, crypto};
|
||||
use crypto::Keccak256;
|
||||
@@ -73,12 +74,12 @@ impl From<Crypto> for String {
|
||||
|
||||
impl Crypto {
|
||||
/// Encrypt account secret
|
||||
pub fn with_secret(secret: &Secret, password: &Password, iterations: u32) -> Result<Self, crypto::Error> {
|
||||
Crypto::with_plain(secret.as_ref(), password, iterations)
|
||||
pub fn with_secret(secret: &Secret, password: &Password, iterations: NonZeroU32) -> Result<Self, crypto::Error> {
|
||||
Crypto::with_plain(&*secret, password, iterations)
|
||||
}
|
||||
|
||||
/// Encrypt custom plain data
|
||||
pub fn with_plain(plain: &[u8], password: &Password, iterations: u32) -> Result<Self, crypto::Error> {
|
||||
pub fn with_plain(plain: &[u8], password: &Password, iterations: NonZeroU32) -> Result<Self, crypto::Error> {
|
||||
let salt: [u8; 32] = Random::random();
|
||||
let iv: [u8; 16] = Random::random();
|
||||
|
||||
@@ -159,13 +160,17 @@ impl Crypto {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethkey::{Generator, Random};
|
||||
use super::{Crypto, Error};
|
||||
use super::{Crypto, Error, NonZeroU32};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_create() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &passwd, 10240).unwrap();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &passwd, *ITERATIONS).unwrap();
|
||||
let secret = crypto.secret(&passwd).unwrap();
|
||||
assert_eq!(keypair.secret(), &secret);
|
||||
}
|
||||
@@ -173,7 +178,7 @@ mod tests {
|
||||
#[test]
|
||||
fn crypto_with_secret_invalid_password() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), 10240).unwrap();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
|
||||
assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword))
|
||||
}
|
||||
|
||||
@@ -181,7 +186,7 @@ mod tests {
|
||||
fn crypto_with_null_plain_data() {
|
||||
let original_data = b"";
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
@@ -190,7 +195,7 @@ mod tests {
|
||||
fn crypto_with_tiny_plain_data() {
|
||||
let original_data = b"{}";
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
@@ -199,7 +204,7 @@ mod tests {
|
||||
fn crypto_with_huge_plain_data() {
|
||||
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data, &passwd, 10240).unwrap();
|
||||
let crypto = Crypto::with_plain(&original_data, &passwd, *ITERATIONS).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(&original_data, &decrypted_data);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use json;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Prf {
|
||||
@@ -23,7 +24,7 @@ pub enum Prf {
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Pbkdf2 {
|
||||
pub c: u32,
|
||||
pub c: NonZeroU32,
|
||||
pub dklen: u32,
|
||||
pub prf: Prf,
|
||||
pub salt: Vec<u8>,
|
||||
|
||||
@@ -20,6 +20,7 @@ use {json, Error};
|
||||
use account::Version;
|
||||
use crypto;
|
||||
use super::crypto::Crypto;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// Account representation.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -59,7 +60,7 @@ impl SafeAccount {
|
||||
keypair: &KeyPair,
|
||||
id: [u8; 16],
|
||||
password: &Password,
|
||||
iterations: u32,
|
||||
iterations: NonZeroU32,
|
||||
name: String,
|
||||
meta: String
|
||||
) -> Result<Self, crypto::Error> {
|
||||
@@ -135,7 +136,7 @@ impl SafeAccount {
|
||||
}
|
||||
|
||||
/// Create a new `VaultKeyFile` from the given `self`
|
||||
pub fn into_vault_file(self, iterations: u32, password: &Password) -> Result<json::VaultKeyFile, Error> {
|
||||
pub fn into_vault_file(self, iterations: NonZeroU32, password: &Password) -> Result<json::VaultKeyFile, Error> {
|
||||
let meta_plain = json::VaultKeyMeta {
|
||||
address: self.address.into(),
|
||||
name: Some(self.name),
|
||||
@@ -177,7 +178,7 @@ impl SafeAccount {
|
||||
}
|
||||
|
||||
/// Change account's password.
|
||||
pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: u32) -> Result<Self, Error> {
|
||||
pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: NonZeroU32) -> Result<Self, Error> {
|
||||
let secret = self.crypto.secret(old_password)?;
|
||||
let result = SafeAccount {
|
||||
id: self.id.clone(),
|
||||
@@ -200,14 +201,19 @@ impl SafeAccount {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethkey::{Generator, Random, verify_public, Message};
|
||||
use super::SafeAccount;
|
||||
use super::{SafeAccount, NonZeroU32};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_public() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 10240, "Test".to_owned(), "{}".to_owned());
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
|
||||
let signature = account.unwrap().sign(&password, &message).unwrap();
|
||||
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
||||
}
|
||||
@@ -217,10 +223,9 @@ mod tests {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let first_password = "hello world".into();
|
||||
let sec_password = "this is sparta".into();
|
||||
let i = 10240;
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, i, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let new_account = account.change_password(&first_password, &sec_password, i).unwrap();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let new_account = account.change_password(&first_password, &sec_password, *ITERATIONS).unwrap();
|
||||
assert!(account.sign(&first_password, &message).is_ok());
|
||||
assert!(account.sign(&sec_password, &message).is_err());
|
||||
assert!(new_account.sign(&first_password, &message).is_err());
|
||||
|
||||
@@ -61,6 +61,7 @@ pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_fil
|
||||
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||
#[cfg(unix)]
|
||||
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
use libc;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
fs::OpenOptions::new()
|
||||
@@ -82,6 +83,7 @@ pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result
|
||||
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||
#[cfg(unix)]
|
||||
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
use libc;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let file = fs::File::create(file_path)?;
|
||||
@@ -354,11 +356,16 @@ mod test {
|
||||
extern crate tempdir;
|
||||
|
||||
use std::{env, fs};
|
||||
use std::num::NonZeroU32;
|
||||
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
|
||||
use account::SafeAccount;
|
||||
use ethkey::{Random, Generator};
|
||||
use self::tempdir::TempDir;
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_new_account() {
|
||||
// given
|
||||
@@ -369,7 +376,7 @@ mod test {
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
|
||||
let res = directory.insert(account.unwrap());
|
||||
|
||||
// then
|
||||
@@ -390,7 +397,7 @@ mod test {
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let filename = "test".to_string();
|
||||
let dedup = true;
|
||||
|
||||
@@ -426,7 +433,7 @@ mod test {
|
||||
|
||||
// and when
|
||||
let before_root_items_count = fs::read_dir(&dir).unwrap().count();
|
||||
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, 1024));
|
||||
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, *ITERATIONS));
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
@@ -434,7 +441,7 @@ mod test {
|
||||
assert!(after_root_items_count > before_root_items_count);
|
||||
|
||||
// and when
|
||||
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, 1024));
|
||||
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, *ITERATIONS));
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
@@ -451,8 +458,9 @@ mod test {
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
||||
let vault_provider = directory.as_vault_provider().unwrap();
|
||||
vault_provider.create("vault1", VaultKey::new(&"password1".into(), 1)).unwrap();
|
||||
vault_provider.create("vault2", VaultKey::new(&"password2".into(), 1)).unwrap();
|
||||
let iter = NonZeroU32::new(1).expect("1 > 0; qed");
|
||||
vault_provider.create("vault1", VaultKey::new(&"password1".into(), iter)).unwrap();
|
||||
vault_provider.create("vault2", VaultKey::new(&"password2".into(), iter)).unwrap();
|
||||
|
||||
// then
|
||||
let vaults = vault_provider.list_vaults().unwrap();
|
||||
@@ -474,7 +482,7 @@ mod test {
|
||||
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "test pass".into();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
|
||||
directory.insert(account.unwrap()).expect("Account should be inserted ok");
|
||||
|
||||
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");
|
||||
|
||||
@@ -68,7 +68,7 @@ impl KeyDirectory for MemoryDirectory {
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
let mut val = 0u64;
|
||||
let accounts = self.accounts.read();
|
||||
for acc in accounts.keys() { val = val ^ acc.to_low_u64_be() }
|
||||
for acc in accounts.keys() { val = val ^ acc.low_u64() }
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
//! Accounts Directory
|
||||
|
||||
use ethkey::Password;
|
||||
use std::num::NonZeroU32;
|
||||
use std::path::{PathBuf};
|
||||
use {SafeAccount, Error};
|
||||
|
||||
@@ -41,7 +42,7 @@ pub struct VaultKey {
|
||||
/// Vault password
|
||||
pub password: Password,
|
||||
/// Number of iterations to produce a derived key from password
|
||||
pub iterations: u32,
|
||||
pub iterations: NonZeroU32,
|
||||
}
|
||||
|
||||
/// Keys directory
|
||||
@@ -96,7 +97,7 @@ pub use self::vault::VaultDiskDirectory;
|
||||
|
||||
impl VaultKey {
|
||||
/// Create new vault key
|
||||
pub fn new(password: &Password, iterations: u32) -> Self {
|
||||
pub fn new(password: &Password, iterations: NonZeroU32) -> Self {
|
||||
VaultKey {
|
||||
password: password.clone(),
|
||||
iterations: iterations,
|
||||
|
||||
@@ -282,11 +282,17 @@ mod test {
|
||||
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroU32;
|
||||
use std::path::PathBuf;
|
||||
use super::VaultKey;
|
||||
use super::{VAULT_FILE_NAME, check_vault_name, make_vault_dir_path, create_vault_file, read_vault_file, VaultDiskDirectory};
|
||||
use self::tempdir::TempDir;
|
||||
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vault_name_succeeds() {
|
||||
assert!(check_vault_name("vault"));
|
||||
@@ -325,7 +331,7 @@ mod test {
|
||||
fn create_vault_file_succeeds() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let mut vault_dir: PathBuf = temp_path.path().into();
|
||||
vault_dir.push("vault");
|
||||
fs::create_dir_all(&vault_dir).unwrap();
|
||||
@@ -344,7 +350,7 @@ mod test {
|
||||
fn read_vault_file_succeeds() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#;
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_file_path: PathBuf = dir.clone();
|
||||
@@ -365,7 +371,7 @@ mod test {
|
||||
fn read_vault_file_fails() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password1".into(), 1024);
|
||||
let key = VaultKey::new(&"password1".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_file_path: PathBuf = dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
@@ -394,7 +400,7 @@ mod test {
|
||||
fn vault_directory_can_be_created() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
|
||||
// when
|
||||
@@ -414,7 +420,7 @@ mod test {
|
||||
fn vault_directory_cannot_be_created_if_already_exists() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_dir = dir.clone();
|
||||
vault_dir.push("vault");
|
||||
@@ -431,7 +437,7 @@ mod test {
|
||||
fn vault_directory_cannot_be_opened_if_not_exists() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
|
||||
// when
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::num::NonZeroU32;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
use crypto::KEY_ITERATIONS;
|
||||
use random::Random;
|
||||
use ethkey::{self, Signature, Password, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair};
|
||||
use accounts_dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
|
||||
@@ -29,6 +29,12 @@ use presale::PresaleWallet;
|
||||
use json::{self, Uuid, OpaqueKeyFile};
|
||||
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret};
|
||||
|
||||
|
||||
lazy_static! {
|
||||
static ref KEY_ITERATIONS: NonZeroU32 =
|
||||
NonZeroU32::new(crypto::KEY_ITERATIONS as u32).expect("KEY_ITERATIONS > 0; qed");
|
||||
}
|
||||
|
||||
/// Accounts store.
|
||||
pub struct EthStore {
|
||||
store: EthMultiStore,
|
||||
@@ -37,11 +43,11 @@ pub struct EthStore {
|
||||
impl EthStore {
|
||||
/// Open a new accounts store with given key directory backend.
|
||||
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
||||
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
|
||||
Self::open_with_iterations(directory, *KEY_ITERATIONS)
|
||||
}
|
||||
|
||||
/// Open a new account store with given key directory backend and custom number of iterations.
|
||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: NonZeroU32) -> Result<Self, Error> {
|
||||
Ok(EthStore {
|
||||
store: EthMultiStore::open_with_iterations(directory, iterations)?,
|
||||
})
|
||||
@@ -257,7 +263,7 @@ impl SecretStore for EthStore {
|
||||
/// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address`
|
||||
pub struct EthMultiStore {
|
||||
dir: Box<KeyDirectory>,
|
||||
iterations: u32,
|
||||
iterations: NonZeroU32,
|
||||
// order lock: cache, then vaults
|
||||
cache: RwLock<BTreeMap<StoreAccountRef, Vec<SafeAccount>>>,
|
||||
vaults: Mutex<HashMap<String, Box<VaultKeyDirectory>>>,
|
||||
@@ -273,11 +279,11 @@ struct Timestamp {
|
||||
impl EthMultiStore {
|
||||
/// Open new multi-accounts store with given key directory backend.
|
||||
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
||||
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
|
||||
Self::open_with_iterations(directory, *KEY_ITERATIONS)
|
||||
}
|
||||
|
||||
/// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys.
|
||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: NonZeroU32) -> Result<Self, Error> {
|
||||
let store = EthMultiStore {
|
||||
dir: directory,
|
||||
vaults: Mutex::new(HashMap::new()),
|
||||
@@ -1084,7 +1090,7 @@ mod tests {
|
||||
SecretVaultRef::Root,
|
||||
&address,
|
||||
&"test".into(),
|
||||
Derivation::HardHash(H256::zero()),
|
||||
Derivation::HardHash(H256::from(0)),
|
||||
).unwrap();
|
||||
|
||||
// there should be 2 accounts in the store
|
||||
|
||||
@@ -41,7 +41,7 @@ impl str::FromStr for Crypto {
|
||||
|
||||
impl From<Crypto> for String {
|
||||
fn from(c: Crypto) -> Self {
|
||||
serde_json::to_string(&c).expect("Serialization cannot fail, because all crypto keys are strings")
|
||||
serde_json::to_string(&c).expect("serialization cannot fail, cause all crypto keys are strings")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU32;
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::de::{Visitor, Error as SerdeError};
|
||||
use super::{Error, Bytes};
|
||||
@@ -108,7 +109,7 @@ impl<'a> Visitor<'a> for PrfVisitor {
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Pbkdf2 {
|
||||
pub c: u32,
|
||||
pub c: NonZeroU32,
|
||||
pub dklen: u32,
|
||||
pub prf: Prf,
|
||||
pub salt: Bytes,
|
||||
|
||||
@@ -41,6 +41,11 @@ impl VaultFile {
|
||||
mod test {
|
||||
use serde_json;
|
||||
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
@@ -51,7 +56,7 @@ mod test {
|
||||
}),
|
||||
ciphertext: "4d6938a1f49b7782".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 1024,
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||
@@ -76,7 +81,7 @@ mod test {
|
||||
}),
|
||||
ciphertext: "4d6938a1f49b7782".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 1024,
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||
|
||||
@@ -106,6 +106,11 @@ mod test {
|
||||
use serde_json;
|
||||
use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf,
|
||||
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
@@ -118,7 +123,7 @@ mod test {
|
||||
}),
|
||||
ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 10240,
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
|
||||
@@ -131,7 +136,7 @@ mod test {
|
||||
}),
|
||||
ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 10240,
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
|
||||
|
||||
@@ -36,6 +36,8 @@ extern crate ethereum_types;
|
||||
extern crate ethkey as _ethkey;
|
||||
extern crate parity_wordlist;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fs;
|
||||
use std::num::NonZeroU32;
|
||||
use std::path::Path;
|
||||
use json;
|
||||
use ethkey::{Address, Secret, KeyPair, Password};
|
||||
@@ -58,7 +59,8 @@ impl PresaleWallet {
|
||||
let mut derived_key = [0u8; 32];
|
||||
let salt = pbkdf2::Salt(password.as_bytes());
|
||||
let sec = pbkdf2::Secret(password.as_bytes());
|
||||
pbkdf2::sha256(2000, salt, sec, &mut derived_key);
|
||||
let iter = NonZeroU32::new(2000).expect("2000 > 0; qed");
|
||||
pbkdf2::sha256(iter, salt, sec, &mut derived_key);
|
||||
|
||||
let mut key = vec![0; self.ciphertext.len()];
|
||||
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::{Rng, RngCore, rngs::OsRng, distributions::Alphanumeric};
|
||||
use rand::{Rng, OsRng};
|
||||
|
||||
pub trait Random {
|
||||
fn random() -> Self where Self: Sized;
|
||||
@@ -41,5 +41,5 @@ impl Random for [u8; 32] {
|
||||
/// Generate a random string of given length.
|
||||
pub fn random_string(length: usize) -> String {
|
||||
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||
rng.sample_iter(&Alphanumeric).take(length).collect()
|
||||
rng.gen_ascii_chars().take(length).collect()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
extern crate rand;
|
||||
extern crate ethstore;
|
||||
extern crate ethereum_types;
|
||||
|
||||
mod util;
|
||||
|
||||
@@ -24,8 +23,6 @@ use ethstore::{EthStore, SimpleSecretStore, SecretVaultRef, StoreAccountRef};
|
||||
use ethstore::ethkey::{Random, Generator, Secret, KeyPair, verify_address};
|
||||
use ethstore::accounts_dir::RootDiskDirectory;
|
||||
use util::TransientDir;
|
||||
use ethereum_types::Address;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn secret_store_create() {
|
||||
@@ -117,9 +114,9 @@ fn secret_store_laod_geth_files() {
|
||||
let dir = RootDiskDirectory::at(test_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap()),
|
||||
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
|
||||
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
|
||||
StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -128,8 +125,8 @@ fn secret_store_load_pat_files() {
|
||||
let dir = RootDiskDirectory::at(pat_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()),
|
||||
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
|
||||
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -143,8 +140,8 @@ fn test_decrypting_files_with_short_ciphertext() {
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts, vec![
|
||||
StoreAccountRef::root(Address::from_str("31e9d1e6d844bd3a536800ef8d8be6a9975db509").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("d1e64e5480bfaf733ba7d48712decb8227797a4e").unwrap()),
|
||||
StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()),
|
||||
StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()),
|
||||
]);
|
||||
|
||||
let message = Default::default();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use rand::{Rng, OsRng};
|
||||
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
|
||||
use ethstore::{Error, SafeAccount};
|
||||
|
||||
|
||||
10
accounts/fake-hardware-wallet/Cargo.toml
Normal file
10
accounts/fake-hardware-wallet/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
description = "Fake hardware-wallet, for OS' that don't support libusb"
|
||||
name = "fake-hardware-wallet"
|
||||
version = "0.0.1"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
101
accounts/fake-hardware-wallet/src/lib.rs
Normal file
101
accounts/fake-hardware-wallet/src/lib.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Dummy module for platforms that does not provide support for hardware wallets (libusb)
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey;
|
||||
|
||||
use std::fmt;
|
||||
use ethereum_types::U256;
|
||||
use ethkey::{Address, Signature};
|
||||
|
||||
pub struct WalletInfo {
|
||||
pub address: Address,
|
||||
pub name: String,
|
||||
pub manufacturer: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// `ErrorType` for devices with no `hardware wallet`
|
||||
pub enum Error {
|
||||
NoWallet,
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
pub struct TransactionInfo {
|
||||
/// Nonce
|
||||
pub nonce: U256,
|
||||
/// Gas price
|
||||
pub gas_price: U256,
|
||||
/// Gas limit
|
||||
pub gas_limit: U256,
|
||||
/// Receiver
|
||||
pub to: Option<Address>,
|
||||
/// Value
|
||||
pub value: U256,
|
||||
/// Data
|
||||
pub data: Vec<u8>,
|
||||
/// Chain ID
|
||||
pub chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
pub enum KeyPath {
|
||||
/// Ethereum.
|
||||
Ethereum,
|
||||
/// Ethereum classic.
|
||||
EthereumClassic,
|
||||
}
|
||||
|
||||
/// `HardwareWalletManager` for devices with no `hardware wallet`
|
||||
pub struct HardwareWalletManager;
|
||||
|
||||
impl HardwareWalletManager {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn set_key_path(&self, _key_path: KeyPath) {}
|
||||
|
||||
pub fn wallet_info(&self, _: &Address) -> Option<WalletInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
|
||||
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result<bool, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn sign_transaction(&self, _address: &Address, _transaction: &TransactionInfo, _rlp_transaction: &[u8]) -> Result<Signature, Error> {
|
||||
Err(Error::NoWallet) }
|
||||
|
||||
pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result<Signature, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "No hardware wallet!!")
|
||||
}
|
||||
}
|
||||
21
accounts/hw/Cargo.toml
Normal file
21
accounts/hw/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
description = "Hardware wallet support."
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "hardware-wallet"
|
||||
version = "1.12.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
parking_lot = "0.7"
|
||||
protobuf = "1.4"
|
||||
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
|
||||
libusb = { git = "https://github.com/paritytech/libusb-rs" }
|
||||
trezor-sys = { git = "https://github.com/paritytech/trezor-sys" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
ethereum-types = "0.4"
|
||||
semver = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-hex = "1.0"
|
||||
534
accounts/hw/src/ledger.rs
Normal file
534
accounts/hw/src/ledger.rs
Normal file
File diff suppressed because one or more lines are too long
402
accounts/hw/src/lib.rs
Normal file
402
accounts/hw/src/lib.rs
Normal file
@@ -0,0 +1,402 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Hardware wallet management.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(warnings)]
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey;
|
||||
extern crate hidapi;
|
||||
extern crate libusb;
|
||||
extern crate parking_lot;
|
||||
extern crate protobuf;
|
||||
extern crate semver;
|
||||
extern crate trezor_sys;
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
#[cfg(test)] extern crate rustc_hex;
|
||||
|
||||
mod ledger;
|
||||
mod trezor;
|
||||
|
||||
use std::sync::{Arc, atomic, atomic::AtomicBool, Weak};
|
||||
use std::{fmt, time::Duration};
|
||||
use std::thread;
|
||||
|
||||
use ethereum_types::U256;
|
||||
use ethkey::{Address, Signature};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00;
|
||||
const HID_USB_DEVICE_CLASS: u8 = 0;
|
||||
const MAX_POLLING_DURATION: Duration = Duration::from_millis(500);
|
||||
const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500);
|
||||
|
||||
/// `HardwareWallet` device
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: String,
|
||||
info: WalletInfo,
|
||||
}
|
||||
|
||||
/// `Wallet` trait
|
||||
pub trait Wallet<'a> {
|
||||
/// Error
|
||||
type Error;
|
||||
/// Transaction data format
|
||||
type Transaction;
|
||||
|
||||
/// Sign transaction data with wallet managing `address`.
|
||||
fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result<Signature, Self::Error>;
|
||||
|
||||
/// Set key derivation path for a chain.
|
||||
fn set_key_path(&self, key_path: KeyPath);
|
||||
|
||||
/// Re-populate device list
|
||||
/// Note, this assumes all devices are iterated over and updated
|
||||
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Self::Error>;
|
||||
|
||||
/// Read device info
|
||||
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Self::Error>;
|
||||
|
||||
/// List connected and acknowledged wallets
|
||||
fn list_devices(&self) -> Vec<WalletInfo>;
|
||||
|
||||
/// List locked wallets
|
||||
/// This may be moved if it is the wrong assumption, for example this is not supported by Ledger
|
||||
/// Then this method return a empty vector
|
||||
fn list_locked_devices(&self) -> Vec<String>;
|
||||
|
||||
/// Get wallet info.
|
||||
fn get_wallet(&self, address: &Address) -> Option<WalletInfo>;
|
||||
|
||||
/// Generate ethereum address for a Wallet
|
||||
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error>;
|
||||
|
||||
/// Open a device using `device path`
|
||||
/// Note, f - is a closure that borrows HidResult<HidDevice>
|
||||
/// HidDevice is in turn a type alias for a `c_void function pointer`
|
||||
/// For further information see:
|
||||
/// * <https://github.com/paritytech/hidapi-rs>
|
||||
/// * <https://github.com/rust-lang/libc>
|
||||
fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error>
|
||||
where F: Fn() -> Result<R, &'static str>;
|
||||
}
|
||||
|
||||
/// Hardware wallet error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Ledger device error.
|
||||
LedgerDevice(ledger::Error),
|
||||
/// Trezor device error
|
||||
TrezorDevice(trezor::Error),
|
||||
/// USB error.
|
||||
Usb(libusb::Error),
|
||||
/// HID error
|
||||
Hid(String),
|
||||
/// Hardware wallet not found for specified key.
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
/// This is the transaction info we need to supply to Trezor message. It's more
|
||||
/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't
|
||||
/// import ethcore here as that would be a circular dependency.
|
||||
pub struct TransactionInfo {
|
||||
/// Nonce
|
||||
pub nonce: U256,
|
||||
/// Gas price
|
||||
pub gas_price: U256,
|
||||
/// Gas limit
|
||||
pub gas_limit: U256,
|
||||
/// Receiver
|
||||
pub to: Option<Address>,
|
||||
/// Value
|
||||
pub value: U256,
|
||||
/// Data
|
||||
pub data: Vec<u8>,
|
||||
/// Chain ID
|
||||
pub chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
/// Hardware wallet information.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WalletInfo {
|
||||
/// Wallet device name.
|
||||
pub name: String,
|
||||
/// Wallet device manufacturer.
|
||||
pub manufacturer: String,
|
||||
/// Wallet device serial number.
|
||||
pub serial: String,
|
||||
/// Ethereum address.
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
/// Key derivation paths used on hardware wallets.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KeyPath {
|
||||
/// Ethereum.
|
||||
Ethereum,
|
||||
/// Ethereum classic.
|
||||
EthereumClassic,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::KeyNotFound => write!(f, "Key not found for given address."),
|
||||
Error::LedgerDevice(ref e) => write!(f, "{}", e),
|
||||
Error::TrezorDevice(ref e) => write!(f, "{}", e),
|
||||
Error::Usb(ref e) => write!(f, "{}", e),
|
||||
Error::Hid(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ledger::Error> for Error {
|
||||
fn from(err: ledger::Error) -> Self {
|
||||
match err {
|
||||
ledger::Error::KeyNotFound => Error::KeyNotFound,
|
||||
_ => Error::LedgerDevice(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<trezor::Error> for Error {
|
||||
fn from(err: trezor::Error) -> Self {
|
||||
match err {
|
||||
trezor::Error::KeyNotFound => Error::KeyNotFound,
|
||||
_ => Error::TrezorDevice(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libusb::Error> for Error {
|
||||
fn from(err: libusb::Error) -> Self {
|
||||
Error::Usb(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum DeviceDirection {
|
||||
/// Device arrived
|
||||
Arrived,
|
||||
/// Device left
|
||||
Left,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceDirection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DeviceDirection::Arrived => write!(f, "arrived"),
|
||||
DeviceDirection::Left => write!(f, "left"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware wallet management interface.
|
||||
pub struct HardwareWalletManager {
|
||||
exiting: Arc<AtomicBool>,
|
||||
ledger: Arc<ledger::Manager>,
|
||||
trezor: Arc<trezor::Manager>,
|
||||
}
|
||||
|
||||
impl HardwareWalletManager {
|
||||
/// Hardware wallet constructor
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let exiting = Arc::new(AtomicBool::new(false));
|
||||
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?));
|
||||
let ledger = ledger::Manager::new(hidapi.clone());
|
||||
let trezor = trezor::Manager::new(hidapi.clone());
|
||||
let usb_context = Arc::new(libusb::Context::new()?);
|
||||
|
||||
let l = ledger.clone();
|
||||
let t = trezor.clone();
|
||||
let exit = exiting.clone();
|
||||
|
||||
// Subscribe to all vendor IDs (VIDs) and product IDs (PIDs)
|
||||
// This means that the `HardwareWalletManager` is responsible to validate the detected device
|
||||
usb_context.register_callback(
|
||||
None, None, Some(HID_USB_DEVICE_CLASS),
|
||||
Box::new(EventHandler::new(
|
||||
Arc::downgrade(&ledger),
|
||||
Arc::downgrade(&trezor)
|
||||
))
|
||||
)?;
|
||||
|
||||
// Hardware event subscriber thread
|
||||
thread::Builder::new()
|
||||
.name("hw_wallet_manager".to_string())
|
||||
.spawn(move || {
|
||||
if let Err(e) = l.update_devices(DeviceDirection::Arrived) {
|
||||
debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e);
|
||||
}
|
||||
if let Err(e) = t.update_devices(DeviceDirection::Arrived) {
|
||||
debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e);
|
||||
}
|
||||
|
||||
while !exit.load(atomic::Ordering::Acquire) {
|
||||
if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) {
|
||||
debug!(target: "hw", "HardwareWalletManager event handler error: {}", e);
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
Ok(Self {
|
||||
exiting,
|
||||
trezor,
|
||||
ledger,
|
||||
})
|
||||
}
|
||||
|
||||
/// Select key derivation path for a chain.
|
||||
/// Currently, only one hard-coded keypath is supported
|
||||
/// It is managed by `ethcore/account_provider`
|
||||
pub fn set_key_path(&self, key_path: KeyPath) {
|
||||
self.ledger.set_key_path(key_path);
|
||||
self.trezor.set_key_path(key_path);
|
||||
}
|
||||
|
||||
/// List connected wallets. This only returns wallets that are ready to be used.
|
||||
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||
let mut wallets = Vec::new();
|
||||
wallets.extend(self.ledger.list_devices());
|
||||
wallets.extend(self.trezor.list_devices());
|
||||
wallets
|
||||
}
|
||||
|
||||
/// Return a list of paths to locked hardware wallets
|
||||
/// This is only applicable to Trezor because Ledger only appears as
|
||||
/// a device when it is unlocked
|
||||
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||
Ok(self.trezor.list_locked_devices())
|
||||
}
|
||||
|
||||
/// Get connected wallet info.
|
||||
pub fn wallet_info(&self, address: &Address) -> Option<WalletInfo> {
|
||||
if let Some(info) = self.ledger.get_wallet(address) {
|
||||
Some(info)
|
||||
} else {
|
||||
self.trezor.get_wallet(address)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign a message with the wallet (only supported by Ledger)
|
||||
pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result<Signature, Error> {
|
||||
if self.ledger.get_wallet(address).is_some() {
|
||||
Ok(self.ledger.sign_message(address, msg)?)
|
||||
} else if self.trezor.get_wallet(address).is_some() {
|
||||
Err(Error::TrezorDevice(trezor::Error::NoSigningMessage))
|
||||
} else {
|
||||
Err(Error::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign transaction data with wallet managing `address`.
|
||||
pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo, encoded_transaction: &[u8]) -> Result<Signature, Error> {
|
||||
if self.ledger.get_wallet(address).is_some() {
|
||||
Ok(self.ledger.sign_transaction(address, encoded_transaction)?)
|
||||
} else if self.trezor.get_wallet(address).is_some() {
|
||||
Ok(self.trezor.sign_transaction(address, t_info)?)
|
||||
} else {
|
||||
Err(Error::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a pin to a device at a certain path to unlock it
|
||||
/// This is only applicable to Trezor because Ledger only appears as
|
||||
/// a device when it is unlocked
|
||||
pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, Error> {
|
||||
self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HardwareWalletManager {
|
||||
fn drop(&mut self) {
|
||||
// Indicate to the USB Hotplug handler that it
|
||||
// shall terminate but don't wait for it to terminate.
|
||||
// If it doesn't terminate for some reason USB Hotplug events will be handled
|
||||
// even if the HardwareWalletManger has been dropped
|
||||
self.exiting.store(true, atomic::Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware wallet event handler
|
||||
///
|
||||
/// Note, that this runs to completion and race-conditions can't occur but it can
|
||||
/// stop other events for being processed with an infinite loop or similar
|
||||
struct EventHandler {
|
||||
ledger: Weak<ledger::Manager>,
|
||||
trezor: Weak<trezor::Manager>,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
/// Trezor event handler constructor
|
||||
pub fn new(ledger: Weak<ledger::Manager>, trezor: Weak<trezor::Manager>) -> Self {
|
||||
Self { ledger, trezor }
|
||||
}
|
||||
|
||||
fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> {
|
||||
let desc = device.device_descriptor()?;
|
||||
Ok((desc.vendor_id(), desc.product_id()))
|
||||
}
|
||||
}
|
||||
|
||||
impl libusb::Hotplug for EventHandler {
|
||||
fn device_arrived(&mut self, device: libusb::Device) {
|
||||
// Upgrade reference to an Arc
|
||||
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||
// Version ID and Product ID are available
|
||||
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
|
||||
if trezor::is_valid_trezor(vid, pid) {
|
||||
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived) {
|
||||
trace!(target: "hw", "Trezor device was detected but connection failed");
|
||||
}
|
||||
} else if ledger::is_valid_ledger(vid, pid) {
|
||||
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Arrived) {
|
||||
trace!(target: "hw", "Ledger device was detected but connection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_left(&mut self, device: libusb::Device) {
|
||||
// Upgrade reference to an Arc
|
||||
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||
// Version ID and Product ID are available
|
||||
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
|
||||
if trezor::is_valid_trezor(vid, pid) {
|
||||
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Left) {
|
||||
trace!(target: "hw", "Trezor device was detected but disconnection failed");
|
||||
}
|
||||
} else if ledger::is_valid_ledger(vid, pid) {
|
||||
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Left) {
|
||||
trace!(target: "hw", "Ledger device was detected but disconnection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to determine if a device is a valid HID
|
||||
pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool {
|
||||
usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32
|
||||
}
|
||||
463
accounts/hw/src/trezor.rs
Normal file
463
accounts/hw/src/trezor.rs
Normal file
@@ -0,0 +1,463 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Trezor hardware wallet module. Supports Trezor v1.
|
||||
//! See <http://doc.satoshilabs.com/trezor-tech/api-protobuf.html>
|
||||
//! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md>
|
||||
//! for protocol details.
|
||||
|
||||
use std::cmp::{min, max};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::fmt;
|
||||
|
||||
use ethereum_types::{U256, H256, Address};
|
||||
use ethkey::Signature;
|
||||
use hidapi;
|
||||
use libusb;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use protobuf::{self, Message, ProtobufEnum};
|
||||
use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device};
|
||||
use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck};
|
||||
|
||||
/// Trezor v1 vendor ID
|
||||
const TREZOR_VID: u16 = 0x534c;
|
||||
/// Trezor product IDs
|
||||
const TREZOR_PIDS: [u16; 1] = [0x0001];
|
||||
|
||||
const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0
|
||||
const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0
|
||||
|
||||
/// Hardware wallet error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Ethereum wallet protocol error.
|
||||
Protocol(&'static str),
|
||||
/// Hidapi error.
|
||||
Usb(hidapi::HidError),
|
||||
/// Libusb error
|
||||
LibUsb(libusb::Error),
|
||||
/// Device with request key is not available.
|
||||
KeyNotFound,
|
||||
/// Signing has been cancelled by user.
|
||||
UserCancel,
|
||||
/// The Message Type given in the trezor RPC call is not something we recognize
|
||||
BadMessageType,
|
||||
/// Trying to read from a closed device at the given path
|
||||
LockedDevice(String),
|
||||
/// Signing messages are not supported by Trezor
|
||||
NoSigningMessage,
|
||||
/// No device arrived
|
||||
NoDeviceArrived,
|
||||
/// No device left
|
||||
NoDeviceLeft,
|
||||
/// Invalid PID or VID
|
||||
InvalidDevice,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s),
|
||||
Error::Usb(ref e) => write!(f, "USB communication error: {}", e),
|
||||
Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e),
|
||||
Error::KeyNotFound => write!(f, "Key not found"),
|
||||
Error::UserCancel => write!(f, "Operation has been cancelled"),
|
||||
Error::BadMessageType => write!(f, "Bad Message Type in RPC call"),
|
||||
Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s),
|
||||
Error::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"),
|
||||
Error::NoDeviceArrived => write!(f, "No device arrived"),
|
||||
Error::NoDeviceLeft => write!(f, "No device left"),
|
||||
Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hidapi::HidError> for Error {
|
||||
fn from(err: hidapi::HidError) -> Self {
|
||||
Error::Usb(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libusb::Error> for Error {
|
||||
fn from(err: libusb::Error) -> Self {
|
||||
Error::LibUsb(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<protobuf::ProtobufError> for Error {
|
||||
fn from(_: protobuf::ProtobufError) -> Self {
|
||||
Error::Protocol(&"Could not read response from Trezor Device")
|
||||
}
|
||||
}
|
||||
|
||||
/// Trezor device manager
|
||||
pub struct Manager {
|
||||
usb: Arc<Mutex<hidapi::HidApi>>,
|
||||
devices: RwLock<Vec<Device>>,
|
||||
locked_devices: RwLock<Vec<String>>,
|
||||
key_path: RwLock<KeyPath>,
|
||||
}
|
||||
|
||||
/// HID Version used for the Trezor device
|
||||
enum HidVersion {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
/// Create a new instance.
|
||||
pub fn new(usb: Arc<Mutex<hidapi::HidApi>>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
usb,
|
||||
devices: RwLock::new(Vec::new()),
|
||||
locked_devices: RwLock::new(Vec::new()),
|
||||
key_path: RwLock::new(KeyPath::Ethereum),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result<bool, Error> {
|
||||
let unlocked = {
|
||||
let usb = self.usb.lock();
|
||||
let device = self.open_path(|| usb.open_path(&device_path))?;
|
||||
let t = MessageType::MessageType_PinMatrixAck;
|
||||
let mut m = PinMatrixAck::new();
|
||||
m.set_pin(pin.to_string());
|
||||
self.send_device_message(&device, t, &m)?;
|
||||
let (resp_type, _) = self.read_device_response(&device)?;
|
||||
match resp_type {
|
||||
// Getting an Address back means it's unlocked, this is undocumented behavior
|
||||
MessageType::MessageType_EthereumAddress => Ok(true),
|
||||
// Getting anything else means we didn't unlock it
|
||||
_ => Ok(false),
|
||||
|
||||
}
|
||||
};
|
||||
self.update_devices(DeviceDirection::Arrived)?;
|
||||
unlocked
|
||||
}
|
||||
|
||||
fn u256_to_be_vec(&self, val: &U256) -> Vec<u8> {
|
||||
let mut buf = [0_u8; 32];
|
||||
val.to_big_endian(&mut buf);
|
||||
buf.iter().skip_while(|x| **x == 0).cloned().collect()
|
||||
}
|
||||
|
||||
fn signing_loop(&self, handle: &hidapi::HidDevice, chain_id: &Option<u64>, data: &[u8]) -> Result<Signature, Error> {
|
||||
let (resp_type, bytes) = self.read_device_response(&handle)?;
|
||||
match resp_type {
|
||||
MessageType::MessageType_Cancel => Err(Error::UserCancel),
|
||||
MessageType::MessageType_ButtonRequest => {
|
||||
self.send_device_message(handle, MessageType::MessageType_ButtonAck, &ButtonAck::new())?;
|
||||
// Signing loop goes back to the top and reading blocks
|
||||
// for up to 5 minutes waiting for response from the device
|
||||
// if the user doesn't click any button within 5 minutes you
|
||||
// get a signing error and the device sort of locks up on the signing screen
|
||||
self.signing_loop(handle, chain_id, data)
|
||||
}
|
||||
MessageType::MessageType_EthereumTxRequest => {
|
||||
let resp: EthereumTxRequest = protobuf::core::parse_from_bytes(&bytes)?;
|
||||
if resp.has_data_length() {
|
||||
let mut msg = EthereumTxAck::new();
|
||||
let len = resp.get_data_length() as usize;
|
||||
msg.set_data_chunk(data[..len].to_vec());
|
||||
self.send_device_message(handle, MessageType::MessageType_EthereumTxAck, &msg)?;
|
||||
self.signing_loop(handle, chain_id, &data[len..])
|
||||
} else {
|
||||
let v = resp.get_signature_v();
|
||||
let r = H256::from_slice(resp.get_signature_r());
|
||||
let s = H256::from_slice(resp.get_signature_s());
|
||||
if let Some(c_id) = *chain_id {
|
||||
// If there is a chain_id supplied, Trezor will return a v
|
||||
// part of the signature that is already adjusted for EIP-155,
|
||||
// so v' = v + 2 * chain_id + 35, but code further down the
|
||||
// pipeline will already do this transformation, so remove it here
|
||||
let adjustment = 35 + 2 * c_id as u32;
|
||||
Ok(Signature::from_rsv(&r, &s, (max(v, adjustment) - adjustment) as u8))
|
||||
} else {
|
||||
// If there isn't a chain_id, v will be returned as v + 27
|
||||
let adjusted_v = if v < 27 { v } else { v - 27 };
|
||||
Ok(Signature::from_rsv(&r, &s, adjusted_v as u8))
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageType::MessageType_Failure => Err(Error::Protocol("Last message sent to Trezor failed")),
|
||||
_ => Err(Error::Protocol("Unexpected response from Trezor device.")),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_device_message(&self, device: &hidapi::HidDevice, msg_type: MessageType, msg: &Message) -> Result<usize, Error> {
|
||||
let msg_id = msg_type as u16;
|
||||
let mut message = msg.write_to_bytes()?;
|
||||
let msg_size = message.len();
|
||||
let mut data = Vec::new();
|
||||
let hid_version = self.probe_hid_version(device)?;
|
||||
// Magic constants
|
||||
data.push(b'#');
|
||||
data.push(b'#');
|
||||
// Convert msg_id to BE and split into bytes
|
||||
data.push(((msg_id >> 8) & 0xFF) as u8);
|
||||
data.push((msg_id & 0xFF) as u8);
|
||||
// Convert msg_size to BE and split into bytes
|
||||
data.push(((msg_size >> 24) & 0xFF) as u8);
|
||||
data.push(((msg_size >> 16) & 0xFF) as u8);
|
||||
data.push(((msg_size >> 8) & 0xFF) as u8);
|
||||
data.push((msg_size & 0xFF) as u8);
|
||||
data.append(&mut message);
|
||||
while data.len() % 63 > 0 {
|
||||
data.push(0);
|
||||
}
|
||||
let mut total_written = 0;
|
||||
for chunk in data.chunks(63) {
|
||||
let mut padded_chunk = match hid_version {
|
||||
HidVersion::V1 => vec![b'?'],
|
||||
HidVersion::V2 => vec![0, b'?'],
|
||||
};
|
||||
padded_chunk.extend_from_slice(&chunk);
|
||||
total_written += device.write(&padded_chunk)?;
|
||||
}
|
||||
Ok(total_written)
|
||||
}
|
||||
|
||||
fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result<HidVersion, Error> {
|
||||
let mut buf2 = [0xFF_u8; 65];
|
||||
buf2[0] = 0;
|
||||
buf2[1] = 63;
|
||||
let mut buf1 = [0xFF_u8; 64];
|
||||
buf1[0] = 63;
|
||||
if device.write(&buf2)? == 65 {
|
||||
Ok(HidVersion::V2)
|
||||
} else if device.write(&buf1)? == 64 {
|
||||
Ok(HidVersion::V1)
|
||||
} else {
|
||||
Err(Error::Usb("Unable to determine HID Version"))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_device_response(&self, device: &hidapi::HidDevice) -> Result<(MessageType, Vec<u8>), Error> {
|
||||
let protocol_err = Error::Protocol(&"Unexpected wire response from Trezor Device");
|
||||
let mut buf = vec![0; 64];
|
||||
|
||||
let first_chunk = device.read_timeout(&mut buf, 300_000)?;
|
||||
if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' {
|
||||
return Err(protocol_err);
|
||||
}
|
||||
let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?;
|
||||
let msg_size = ((buf[5] as u32 & 0xFF) << 24) + ((buf[6] as u32 & 0xFF) << 16) + ((buf[7] as u32 & 0xFF) << 8) + (buf[8] as u32 & 0xFF);
|
||||
let mut data = Vec::new();
|
||||
data.extend_from_slice(&buf[9..]);
|
||||
while data.len() < (msg_size as usize) {
|
||||
device.read_timeout(&mut buf, 10_000)?;
|
||||
data.extend_from_slice(&buf[1..]);
|
||||
}
|
||||
Ok((msg_type, data[..msg_size as usize].to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Wallet<'a> for Manager {
|
||||
type Error = Error;
|
||||
type Transaction = &'a TransactionInfo;
|
||||
|
||||
fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) ->
|
||||
Result<Signature, Error> {
|
||||
let usb = self.usb.lock();
|
||||
let devices = self.devices.read();
|
||||
let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?;
|
||||
let handle = self.open_path(|| usb.open_path(&device.path))?;
|
||||
let msg_type = MessageType::MessageType_EthereumSignTx;
|
||||
let mut message = EthereumSignTx::new();
|
||||
match *self.key_path.read() {
|
||||
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
||||
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
||||
}
|
||||
message.set_nonce(self.u256_to_be_vec(&t_info.nonce));
|
||||
message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit));
|
||||
message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price));
|
||||
message.set_value(self.u256_to_be_vec(&t_info.value));
|
||||
|
||||
if let Some(addr) = t_info.to {
|
||||
message.set_to(addr.to_vec())
|
||||
}
|
||||
let first_chunk_length = min(t_info.data.len(), 1024);
|
||||
let chunk = &t_info.data[0..first_chunk_length];
|
||||
message.set_data_initial_chunk(chunk.to_vec());
|
||||
message.set_data_length(t_info.data.len() as u32);
|
||||
if let Some(c_id) = t_info.chain_id {
|
||||
message.set_chain_id(c_id as u32);
|
||||
}
|
||||
|
||||
self.send_device_message(&handle, msg_type, &message)?;
|
||||
|
||||
self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..])
|
||||
}
|
||||
|
||||
fn set_key_path(&self, key_path: KeyPath) {
|
||||
*self.key_path.write() = key_path;
|
||||
}
|
||||
|
||||
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Error> {
|
||||
let mut usb = self.usb.lock();
|
||||
usb.refresh_devices();
|
||||
let devices = usb.devices();
|
||||
let num_prev_devices = self.devices.read().len();
|
||||
|
||||
let detected_devices = devices.iter()
|
||||
.filter(|&d| is_valid_trezor(d.vendor_id, d.product_id) &&
|
||||
is_valid_hid_device(d.usage_page, d.interface_number)
|
||||
)
|
||||
.fold(Vec::new(), |mut v, d| {
|
||||
match self.read_device(&usb, &d) {
|
||||
Ok(info) => {
|
||||
trace!(target: "hw", "Found device: {:?}", info);
|
||||
v.push(info);
|
||||
}
|
||||
Err(e) => trace!(target: "hw", "Error reading device info: {}", e),
|
||||
};
|
||||
v
|
||||
});
|
||||
|
||||
let num_curr_devices = detected_devices.len();
|
||||
*self.devices.write() = detected_devices;
|
||||
|
||||
match device_direction {
|
||||
DeviceDirection::Arrived => {
|
||||
if num_curr_devices > num_prev_devices {
|
||||
Ok(num_curr_devices - num_prev_devices)
|
||||
} else {
|
||||
Err(Error::NoDeviceArrived)
|
||||
}
|
||||
}
|
||||
DeviceDirection::Left => {
|
||||
if num_prev_devices > num_curr_devices {
|
||||
Ok(num_prev_devices - num_curr_devices)
|
||||
} else {
|
||||
Err(Error::NoDeviceLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Error> {
|
||||
let handle = self.open_path(|| usb.open_path(&dev_info.path))?;
|
||||
let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned());
|
||||
let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned());
|
||||
let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned());
|
||||
match self.get_address(&handle) {
|
||||
Ok(Some(addr)) => {
|
||||
Ok(Device {
|
||||
path: dev_info.path.clone(),
|
||||
info: WalletInfo {
|
||||
name,
|
||||
manufacturer,
|
||||
serial,
|
||||
address: addr,
|
||||
},
|
||||
})
|
||||
}
|
||||
Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn list_devices(&self) -> Vec<WalletInfo> {
|
||||
self.devices.read().iter().map(|d| d.info.clone()).collect()
|
||||
}
|
||||
|
||||
fn list_locked_devices(&self) -> Vec<String> {
|
||||
(*self.locked_devices.read()).clone()
|
||||
}
|
||||
|
||||
fn get_wallet(&self, address: &Address) -> Option<WalletInfo> {
|
||||
self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone())
|
||||
}
|
||||
|
||||
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Error> {
|
||||
let typ = MessageType::MessageType_EthereumGetAddress;
|
||||
let mut message = EthereumGetAddress::new();
|
||||
match *self.key_path.read() {
|
||||
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
||||
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
||||
}
|
||||
message.set_show_display(false);
|
||||
self.send_device_message(&device, typ, &message)?;
|
||||
|
||||
let (resp_type, bytes) = self.read_device_response(&device)?;
|
||||
match resp_type {
|
||||
MessageType::MessageType_EthereumAddress => {
|
||||
let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?;
|
||||
Ok(Some(From::from(response.get_address())))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn open_path<R, F>(&self, f: F) -> Result<R, Error>
|
||||
where F: Fn() -> Result<R, &'static str>
|
||||
{
|
||||
f().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll the device in maximum `max_polling_duration` if it doesn't succeed
|
||||
pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool {
|
||||
let start_time = Instant::now();
|
||||
while start_time.elapsed() <= *duration {
|
||||
if let Ok(num_devices) = trezor.update_devices(dir) {
|
||||
trace!(target: "hw", "{} Trezor devices {}", num_devices, dir);
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if the detected device is a Trezor device by checking both the product ID and the vendor ID
|
||||
pub fn is_valid_trezor(vid: u16, pid: u16) -> bool {
|
||||
vid == TREZOR_VID && TREZOR_PIDS.contains(&pid)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
/// This test can't be run without an actual trezor device connected
|
||||
/// (and unlocked) attached to the machine that's running the test
|
||||
fn test_signature() {
|
||||
use ethereum_types::Address;
|
||||
use MAX_POLLING_DURATION;
|
||||
use super::HardwareWalletManager;
|
||||
|
||||
let manager = HardwareWalletManager::new().unwrap();
|
||||
|
||||
assert_eq!(try_connect_polling(&manager.trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived), true);
|
||||
|
||||
let addr: Address = manager.list_wallets()
|
||||
.iter()
|
||||
.filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string())
|
||||
.nth(0)
|
||||
.map(|d| d.address)
|
||||
.unwrap();
|
||||
|
||||
let t_info = TransactionInfo {
|
||||
nonce: U256::from(1),
|
||||
gas_price: U256::from(100),
|
||||
gas_limit: U256::from(21_000),
|
||||
to: Some(Address::from(1337)),
|
||||
chain_id: Some(1),
|
||||
value: U256::from(1_000_000),
|
||||
data: (&[1u8; 3000]).to_vec(),
|
||||
};
|
||||
|
||||
let signature = manager.trezor.sign_transaction(&addr, &t_info);
|
||||
assert!(signature.is_ok());
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use ethstore::{Error as SSError};
|
||||
use hardware_wallet::{Error as HardwareError};
|
||||
|
||||
/// Signing error
|
||||
#[derive(Debug)]
|
||||
@@ -25,6 +26,8 @@ pub enum SignError {
|
||||
NotUnlocked,
|
||||
/// Account does not exist.
|
||||
NotFound,
|
||||
/// Low-level hardware device error.
|
||||
Hardware(HardwareError),
|
||||
/// Low-level error from store
|
||||
SStore(SSError),
|
||||
}
|
||||
@@ -34,11 +37,18 @@ impl fmt::Display for SignError {
|
||||
match *self {
|
||||
SignError::NotUnlocked => write!(f, "Account is locked"),
|
||||
SignError::NotFound => write!(f, "Account does not exist"),
|
||||
SignError::Hardware(ref e) => write!(f, "{}", e),
|
||||
SignError::SStore(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HardwareError> for SignError {
|
||||
fn from(e: HardwareError) -> Self {
|
||||
SignError::Hardware(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SSError> for SignError {
|
||||
fn from(e: SSError) -> Self {
|
||||
SignError::SStore(e)
|
||||
|
||||
@@ -22,23 +22,28 @@ mod account_data;
|
||||
mod error;
|
||||
mod stores;
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
extern crate fake_hardware_wallet as hardware_wallet;
|
||||
|
||||
use self::account_data::{Unlock, AccountData};
|
||||
use self::stores::AddressBook;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
use common_types::transaction::{Action, Transaction};
|
||||
use ethkey::{Address, Message, Public, Secret, Password, Random, Generator};
|
||||
use ethstore::accounts_dir::MemoryDirectory;
|
||||
use ethstore::{
|
||||
SimpleSecretStore, SecretStore, EthStore, EthMultiStore,
|
||||
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
|
||||
};
|
||||
use log::warn;
|
||||
use log::{warn, debug};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub use ethkey::Signature;
|
||||
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
|
||||
pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};
|
||||
|
||||
pub use self::account_data::AccountMeta;
|
||||
pub use self::error::SignError;
|
||||
@@ -48,6 +53,10 @@ type AccountToken = Password;
|
||||
/// Account management settings.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AccountProviderSettings {
|
||||
/// Enable hardware wallet support.
|
||||
pub enable_hardware_wallets: bool,
|
||||
/// Use the classic chain key on the hardware wallet.
|
||||
pub hardware_wallet_classic_key: bool,
|
||||
/// Store raw account secret when unlocking the account permanently.
|
||||
pub unlock_keep_secret: bool,
|
||||
/// Disallowed accounts.
|
||||
@@ -67,6 +76,8 @@ pub struct AccountProvider {
|
||||
sstore: Box<SecretStore>,
|
||||
/// Accounts unlocked with rolling tokens
|
||||
transient_sstore: EthMultiStore,
|
||||
/// Accounts in hardware wallets.
|
||||
hardware_store: Option<HardwareWalletManager>,
|
||||
/// When unlocking account permanently we additionally keep a raw secret in memory
|
||||
/// to increase the performance of transaction signing.
|
||||
unlock_keep_secret: bool,
|
||||
@@ -81,6 +92,18 @@ fn transient_sstore() -> EthMultiStore {
|
||||
impl AccountProvider {
|
||||
/// Creates new account provider.
|
||||
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
|
||||
let mut hardware_store = None;
|
||||
|
||||
if settings.enable_hardware_wallets {
|
||||
match HardwareWalletManager::new() {
|
||||
Ok(manager) => {
|
||||
manager.set_key_path(if settings.hardware_wallet_classic_key { KeyPath::EthereumClassic } else { KeyPath::Ethereum });
|
||||
hardware_store = Some(manager)
|
||||
},
|
||||
Err(e) => debug!("Error initializing hardware wallets: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(accounts) = sstore.accounts() {
|
||||
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) {
|
||||
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
|
||||
@@ -98,8 +121,9 @@ impl AccountProvider {
|
||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
address_book: RwLock::new(address_book),
|
||||
sstore,
|
||||
sstore: sstore,
|
||||
transient_sstore: transient_sstore(),
|
||||
hardware_store: hardware_store,
|
||||
unlock_keep_secret: settings.unlock_keep_secret,
|
||||
blacklisted_accounts: settings.blacklisted_accounts,
|
||||
}
|
||||
@@ -113,6 +137,7 @@ impl AccountProvider {
|
||||
address_book: RwLock::new(AddressBook::transient()),
|
||||
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
||||
transient_sstore: transient_sstore(),
|
||||
hardware_store: None,
|
||||
unlock_keep_secret: false,
|
||||
blacklisted_accounts: vec![],
|
||||
}
|
||||
@@ -194,6 +219,34 @@ impl AccountProvider {
|
||||
Ok(self.accounts()?.first().cloned().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns addresses of hardware accounts.
|
||||
pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
if let Some(accounts) = self.hardware_store.as_ref().map(|h| h.list_wallets()) {
|
||||
if !accounts.is_empty() {
|
||||
return Ok(accounts.into_iter().map(|a| a.address).collect());
|
||||
}
|
||||
}
|
||||
Err(Error::Custom("No hardware wallet accounts were found".into()))
|
||||
}
|
||||
|
||||
/// Get a list of paths to locked hardware wallets
|
||||
pub fn locked_hardware_accounts(&self) -> Result<Vec<String>, SignError> {
|
||||
match self.hardware_store.as_ref().map(|h| h.list_locked_wallets()) {
|
||||
None => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(SignError::Hardware(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide a pin to a locked hardware wallet on USB path to unlock it
|
||||
pub fn hardware_pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, SignError> {
|
||||
match self.hardware_store.as_ref().map(|h| h.pin_matrix_ack(path, pin)) {
|
||||
None => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(SignError::Hardware(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.address_book.read().get()
|
||||
@@ -224,14 +277,36 @@ impl AccountProvider {
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Returns each hardware account along with name and meta.
|
||||
pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
||||
let r = self.hardware_accounts()?
|
||||
.into_iter()
|
||||
.map(|address| (address.clone(), self.account_meta(address).ok().unwrap_or_default()))
|
||||
.collect();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Returns each hardware account along with name and meta.
|
||||
pub fn is_hardware_address(&self, address: &Address) -> bool {
|
||||
self.hardware_store.as_ref().and_then(|s| s.wallet_info(address)).is_some()
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
Ok(AccountMeta {
|
||||
name: self.sstore.name(&account)?,
|
||||
meta: self.sstore.meta(&account)?,
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||
})
|
||||
if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) {
|
||||
Ok(AccountMeta {
|
||||
name: info.name,
|
||||
meta: info.manufacturer,
|
||||
uuid: None,
|
||||
})
|
||||
} else {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
Ok(AccountMeta {
|
||||
name: self.sstore.name(&account)?,
|
||||
meta: self.sstore.meta(&account)?,
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns account public key.
|
||||
@@ -295,7 +370,10 @@ impl AccountProvider {
|
||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||
}
|
||||
|
||||
let data = AccountData { unlock, password };
|
||||
let data = AccountData {
|
||||
unlock: unlock,
|
||||
password: password,
|
||||
};
|
||||
|
||||
unlocked.insert(account, data);
|
||||
Ok(())
|
||||
@@ -497,6 +575,36 @@ impl AccountProvider {
|
||||
self.sstore.set_vault_meta(name, meta)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Sign message with hardware wallet.
|
||||
pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result<Signature, SignError> {
|
||||
match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) {
|
||||
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(From::from(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign transaction with hardware wallet.
|
||||
pub fn sign_transaction_with_hardware(&self, address: &Address, transaction: &Transaction, chain_id: Option<u64>, rlp_encoded_transaction: &[u8]) -> Result<Signature, SignError> {
|
||||
let t_info = TransactionInfo {
|
||||
nonce: transaction.nonce,
|
||||
gas_price: transaction.gas_price,
|
||||
gas_limit: transaction.gas,
|
||||
to: match transaction.action {
|
||||
Action::Create => None,
|
||||
Action::Call(ref to) => Some(to.clone()),
|
||||
},
|
||||
value: transaction.value,
|
||||
data: transaction.data.to_vec(),
|
||||
chain_id: chain_id,
|
||||
};
|
||||
match self.hardware_store.as_ref().map(|s| s.sign_transaction(&address, &t_info, rlp_encoded_transaction)) {
|
||||
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(From::from(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -528,7 +636,7 @@ mod tests {
|
||||
let derived_addr = ap.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(999)),
|
||||
Derivation::SoftHash(H256::from(999)),
|
||||
false,
|
||||
).expect("Derivation should not fail");
|
||||
|
||||
@@ -546,7 +654,7 @@ mod tests {
|
||||
let derived_addr = ap.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(999)),
|
||||
Derivation::SoftHash(H256::from(999)),
|
||||
true,
|
||||
).expect("Derivation should not fail");
|
||||
|
||||
@@ -567,7 +675,7 @@ mod tests {
|
||||
let derived_addr = ap.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(1999)),
|
||||
Derivation::SoftHash(H256::from(1999)),
|
||||
true,
|
||||
).expect("Derivation should not fail");
|
||||
ap.unlock_account_permanently(derived_addr, "base".into())
|
||||
@@ -579,7 +687,7 @@ mod tests {
|
||||
let signed_msg2 = ap.sign_derived(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(1999)),
|
||||
Derivation::SoftHash(H256::from(1999)),
|
||||
msg,
|
||||
).expect("Derived signing with existing unlocked account should not fail");
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AddressBook, Address};
|
||||
use super::AddressBook;
|
||||
use std::collections::HashMap;
|
||||
use tempdir::TempDir;
|
||||
use crate::account_data::AccountMeta;
|
||||
@@ -162,12 +162,12 @@ mod tests {
|
||||
fn should_save_and_reload_address_book() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
b.set_name(Address::from_low_u64_be(1), "One".to_owned());
|
||||
b.set_meta(Address::from_low_u64_be(1), "{1:1}".to_owned());
|
||||
b.set_name(1.into(), "One".to_owned());
|
||||
b.set_meta(1.into(), "{1:1}".to_owned());
|
||||
let b = AddressBook::new(tempdir.path());
|
||||
assert_eq!(b.get(), vec![
|
||||
(Address::from_low_u64_be(1), AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None})
|
||||
].into_iter().collect::<HashMap<_, _>>());
|
||||
(1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None})
|
||||
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -175,15 +175,15 @@ mod tests {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
|
||||
b.set_name(Address::from_low_u64_be(1), "One".to_owned());
|
||||
b.set_name(Address::from_low_u64_be(2), "Two".to_owned());
|
||||
b.set_name(Address::from_low_u64_be(3), "Three".to_owned());
|
||||
b.remove(Address::from_low_u64_be(2).into());
|
||||
b.set_name(1.into(), "One".to_owned());
|
||||
b.set_name(2.into(), "Two".to_owned());
|
||||
b.set_name(3.into(), "Three".to_owned());
|
||||
b.remove(2.into());
|
||||
|
||||
let b = AddressBook::new(tempdir.path());
|
||||
assert_eq!(b.get(), vec![
|
||||
(Address::from_low_u64_be(1), AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
(Address::from_low_u64_be(3), AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
].into_iter().collect::<HashMap<_, _>>());
|
||||
(1, AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
(3, AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
futures = "0.1"
|
||||
rpassword = "1.0"
|
||||
parity-rpc = { path = "../rpc" }
|
||||
|
||||
@@ -7,15 +7,15 @@ version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
futures = "0.1"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
url = "2.1.0"
|
||||
url = "1.2.0"
|
||||
matches = "0.1"
|
||||
parking_lot = "0.9"
|
||||
jsonrpc-core = "14.0.3"
|
||||
jsonrpc-ws-server = "14.0.3"
|
||||
parking_lot = "0.7"
|
||||
jsonrpc-core = "10.0.1"
|
||||
jsonrpc-ws-server = "10.0.1"
|
||||
parity-rpc = { path = "../../rpc" }
|
||||
keccak-hash = "0.2.0"
|
||||
keccak-hash = "0.1"
|
||||
|
||||
@@ -7,15 +7,15 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
crunchy = "0.1.0"
|
||||
either = "1.0.0"
|
||||
ethereum-types = "0.6.0"
|
||||
keccak-hash = "0.2.0"
|
||||
ethereum-types = "0.4"
|
||||
keccak-hash = "0.1"
|
||||
log = "0.4"
|
||||
memmap = "0.6"
|
||||
parking_lot = "0.9"
|
||||
parking_lot = "0.7"
|
||||
primal = "0.2.3"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
criterion = "0.2"
|
||||
rustc-hex = "1.0"
|
||||
serde_json = "1.0"
|
||||
tempdir = "0.3"
|
||||
|
||||
@@ -52,14 +52,14 @@ mod progpow;
|
||||
pub use cache::{NodeCacheBuilder, OptimizeFor};
|
||||
pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number};
|
||||
use compute::Light;
|
||||
use ethereum_types::{BigEndianHash, U256, U512};
|
||||
use ethereum_types::{U256, U512};
|
||||
use keccak::H256;
|
||||
use parking_lot::Mutex;
|
||||
pub use seed_compute::SeedHashCompute;
|
||||
pub use shared::ETHASH_EPOCH_LENGTH;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
struct LightCache {
|
||||
@@ -161,12 +161,12 @@ impl EthashManager {
|
||||
|
||||
/// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`.
|
||||
pub fn boundary_to_difficulty(boundary: ðereum_types::H256) -> U256 {
|
||||
difficulty_to_boundary_aux(&boundary.into_uint())
|
||||
difficulty_to_boundary_aux(&**boundary)
|
||||
}
|
||||
|
||||
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
|
||||
pub fn difficulty_to_boundary(difficulty: &U256) -> ethereum_types::H256 {
|
||||
BigEndianHash::from_uint(&difficulty_to_boundary_aux(difficulty))
|
||||
difficulty_to_boundary_aux(difficulty).into()
|
||||
}
|
||||
|
||||
fn difficulty_to_boundary_aux<T: Into<U512>>(difficulty: T) -> ethereum_types::U256 {
|
||||
@@ -177,8 +177,8 @@ fn difficulty_to_boundary_aux<T: Into<U512>>(difficulty: T) -> ethereum_types::U
|
||||
if difficulty == U512::one() {
|
||||
U256::max_value()
|
||||
} else {
|
||||
const PROOF: &str = "difficulty > 1, so result never overflows 256 bits; qed";
|
||||
U256::try_from((U512::one() << 256) / difficulty).expect(PROOF)
|
||||
// difficulty > 1, so result should never overflow 256 bits
|
||||
U256::from((U512::one() << 256) / difficulty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,10 +203,10 @@ fn test_lru() {
|
||||
|
||||
#[test]
|
||||
fn test_difficulty_to_boundary() {
|
||||
use ethereum_types::{H256, BigEndianHash};
|
||||
use ethereum_types::H256;
|
||||
use std::str::FromStr;
|
||||
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(1)), BigEndianHash::from_uint(&U256::max_value()));
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value()));
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||
@@ -220,18 +220,9 @@ fn test_difficulty_to_boundary_regression() {
|
||||
// https://github.com/paritytech/parity-ethereum/issues/8397
|
||||
for difficulty in 1..9 {
|
||||
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into())));
|
||||
assert_eq!(
|
||||
H256::from_low_u64_be(difficulty),
|
||||
difficulty_to_boundary(&boundary_to_difficulty(&H256::from_low_u64_be(difficulty))),
|
||||
);
|
||||
assert_eq!(
|
||||
U256::from(difficulty),
|
||||
boundary_to_difficulty(&BigEndianHash::from_uint(&boundary_to_difficulty(&H256::from_low_u64_be(difficulty)))),
|
||||
);
|
||||
assert_eq!(
|
||||
H256::from_low_u64_be(difficulty),
|
||||
difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into_uint()),
|
||||
);
|
||||
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into())));
|
||||
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into()));
|
||||
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,5 +235,5 @@ fn test_difficulty_to_boundary_panics_on_zero() {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_boundary_to_difficulty_panics_on_zero() {
|
||||
boundary_to_difficulty(ðereum_types::H256::zero());
|
||||
boundary_to_difficulty(ðereum_types::H256::from(0));
|
||||
}
|
||||
|
||||
@@ -14,22 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! ProgPoW (Programmatic Proof-of-Work) is the Ethereum network's proposed new Application-Specific Integrated
|
||||
//! Circuit (ASIC) resistant Proof-of-Work mining algorithm.
|
||||
//!
|
||||
//! ProgPoW's aim is to reduce the efficiencies of specialized mining devices known as ASIC chips
|
||||
//! (and accelerated GPU-based setups), and to maximize the performance of General Purpose Hardware (GPUs) to enable
|
||||
//! more users to compete for new cryptocurrency awarded by the protocol.
|
||||
//!
|
||||
//! ASIC chips are those that are tailored to efficiently mining cryptocurrency based on a specific hashing algorithm.
|
||||
//!
|
||||
//! GPU mining setups are less specialised are struggle to compete for mining rewards.
|
||||
//!
|
||||
//! It would be a change from Ethereum's current PoW mining algorithm known as Ethash.
|
||||
//!
|
||||
//! ProgPoW audits have been proposed to analyse the efficiency of a ProgPoW ASICs over
|
||||
//! GPUs and analysis of the economic impact on the Ethereum protocol.
|
||||
|
||||
use compute::{FNV_PRIME, calculate_dag_item};
|
||||
use keccak::H256;
|
||||
use shared::{ETHASH_ACCESSES, ETHASH_MIX_BYTES, Node, get_data_size};
|
||||
|
||||
@@ -7,64 +7,62 @@ version = "1.12.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
account-db = { path = "account-db" }
|
||||
ansi_term = "0.11"
|
||||
ansi_term = "0.10"
|
||||
blooms-db = { path = "../util/blooms-db", optional = true }
|
||||
bn = { git = "https://github.com/paritytech/bn", default-features = false }
|
||||
byteorder = "1.0"
|
||||
common-types = { path = "types" }
|
||||
crossbeam-utils = "0.6"
|
||||
derive_more = "0.14.0"
|
||||
env_logger = { version = "0.5", optional = true }
|
||||
ethabi = "8.0"
|
||||
ethabi-contract = "8.0"
|
||||
ethabi-derive = "8.0"
|
||||
error-chain = { version = "0.12", default-features = false }
|
||||
ethabi = "6.0"
|
||||
ethabi-contract = "6.0"
|
||||
ethabi-derive = "6.0"
|
||||
ethash = { path = "../ethash" }
|
||||
ethcore-blockchain = { path = "./blockchain" }
|
||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
||||
ethcore-builtin = { path = "./builtin" }
|
||||
ethcore-call-contract = { path = "./call-contract" }
|
||||
ethcore-db = { path = "./db" }
|
||||
ethcore-io = { path = "../util/io" }
|
||||
ethcore-miner = { path = "../miner" }
|
||||
ethcore-stratum = { path = "../miner/stratum", optional = true }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
ethjson = { path = "../json" }
|
||||
ethkey = { path = "../accounts/ethkey" }
|
||||
evm = { path = "evm" }
|
||||
futures = "0.1"
|
||||
hash-db = "0.12.4"
|
||||
parity-util-mem = "0.1"
|
||||
hash-db = "0.11.0"
|
||||
heapsize = "0.4"
|
||||
itertools = "0.5"
|
||||
journaldb = { path = "../util/journaldb" }
|
||||
keccak-hash = "0.2.0"
|
||||
keccak-hash = "0.1"
|
||||
keccak-hasher = { path = "../util/keccak-hasher" }
|
||||
kvdb = "0.1"
|
||||
kvdb-memorydb = "0.1"
|
||||
kvdb-rocksdb = { version = "0.1.5", optional = true }
|
||||
kvdb-rocksdb = { version = "0.1.3", optional = true }
|
||||
lazy_static = "1.2.0"
|
||||
len-caching-lock = { path = "../util/len-caching-lock" }
|
||||
log = "0.4"
|
||||
lru-cache = "0.1"
|
||||
macros = { path = "../util/macros" }
|
||||
memory-cache = { path = "../util/memory-cache" }
|
||||
memory-db = "0.12.4"
|
||||
memory-db = "0.11.0"
|
||||
num = { version = "0.1", default-features = false, features = ["bigint"] }
|
||||
num_cpus = "1.2"
|
||||
parity-bytes = "0.1"
|
||||
parity-crypto = "0.3.0"
|
||||
parity-snappy = "0.1"
|
||||
parking_lot = "0.9"
|
||||
pod-account = { path = "pod-account" }
|
||||
trie-db = "0.12.4"
|
||||
parking_lot = "0.7"
|
||||
trie-db = "0.11.0"
|
||||
patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" }
|
||||
rand = "0.6"
|
||||
rand = "0.4"
|
||||
rayon = "1.1"
|
||||
rlp = "0.4.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../util/rlp-derive" }
|
||||
rustc-hex = "1"
|
||||
scopeguard = "1.0.0"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
state-account = { path = "state-account" }
|
||||
stats = { path = "../util/stats" }
|
||||
tempdir = { version = "0.3", optional = true }
|
||||
tempdir = {version="0.3", optional = true}
|
||||
time-utils = { path = "../util/time-utils" }
|
||||
trace-time = "0.1"
|
||||
triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" }
|
||||
@@ -72,23 +70,18 @@ unexpected = { path = "../util/unexpected" }
|
||||
using_queue = { path = "../miner/using-queue" }
|
||||
vm = { path = "vm" }
|
||||
wasm = { path = "wasm" }
|
||||
rand_xorshift = "0.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
blooms-db = { path = "../util/blooms-db" }
|
||||
common-types = { path = "types", features = ["test-helpers"] }
|
||||
criterion = "0.3"
|
||||
criterion = "0.2"
|
||||
env_logger = "0.5"
|
||||
ethcore-accounts = { path = "../accounts" }
|
||||
ethjson = { path = "../json", features = ["test-helpers"] }
|
||||
ethkey = { path = "../accounts/ethkey" }
|
||||
fetch = { path = "../util/fetch" }
|
||||
kvdb-rocksdb = "0.1.5"
|
||||
kvdb-rocksdb = "0.1.3"
|
||||
parity-runtime = { path = "../util/runtime" }
|
||||
rlp_compress = { path = "../util/rlp-compress" }
|
||||
serde_json = "1.0"
|
||||
tempdir = "0.3"
|
||||
trie-standardmap = "0.12.4"
|
||||
trie-standardmap = "0.1"
|
||||
|
||||
[features]
|
||||
parity = ["work-notify", "price-info", "stratum"]
|
||||
@@ -113,6 +106,8 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"]
|
||||
slow-blocks = []
|
||||
# Run JSON consensus tests.
|
||||
json-tests = ["env_logger", "test-helpers", "to-pod-full"]
|
||||
# Skip JSON consensus tests with pending issues.
|
||||
ci-skip-tests = []
|
||||
# Run memory/cpu heavy tests.
|
||||
test-heavy = []
|
||||
# Compile test helpers
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
description = "DB backend wrapper for Account trie"
|
||||
name = "account-db"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.6"
|
||||
hash-db = "0.12.4"
|
||||
keccak-hash = "0.2.0"
|
||||
keccak-hasher = { path = "../../util/keccak-hasher" }
|
||||
kvdb = "0.1"
|
||||
rlp = "0.4"
|
||||
@@ -21,21 +21,20 @@ extern crate criterion;
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate ethcore;
|
||||
extern crate ethcore_builtin as builtin;
|
||||
extern crate ethereum_types;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate rustc_hex;
|
||||
|
||||
use criterion::{Criterion, Bencher};
|
||||
use bytes::BytesRef;
|
||||
use builtin::Builtin;
|
||||
use ethcore::machine::Machine;
|
||||
use ethereum_types::H160;
|
||||
use ethcore::builtin::Builtin;
|
||||
use ethcore::machine::EthereumMachine;
|
||||
use ethereum_types::U256;
|
||||
use ethcore::ethereum::new_byzantium_test_machine;
|
||||
use rustc_hex::FromHex;
|
||||
|
||||
lazy_static! {
|
||||
static ref BYZANTIUM_MACHINE: Machine = new_byzantium_test_machine();
|
||||
static ref BYZANTIUM_MACHINE: EthereumMachine = new_byzantium_test_machine();
|
||||
}
|
||||
|
||||
struct BuiltinBenchmark<'a> {
|
||||
@@ -47,9 +46,8 @@ struct BuiltinBenchmark<'a> {
|
||||
impl<'a> BuiltinBenchmark<'a> {
|
||||
fn new(builtin_address: &'static str, input: &str, expected: &str) -> BuiltinBenchmark<'a> {
|
||||
let builtins = BYZANTIUM_MACHINE.builtins();
|
||||
use std::str::FromStr;
|
||||
let addr = H160::from_str(builtin_address).unwrap();
|
||||
let builtin = builtins.get(&addr).unwrap().clone();
|
||||
|
||||
let builtin = builtins.get(&builtin_address.into()).unwrap().clone();
|
||||
let input = FromHex::from_hex(input).unwrap();
|
||||
let expected = FromHex::from_hex(expected).unwrap();
|
||||
|
||||
@@ -58,6 +56,10 @@ impl<'a> BuiltinBenchmark<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_cost(&self) -> U256 {
|
||||
self.builtin.cost(&self.input)
|
||||
}
|
||||
|
||||
fn run(&self, b: &mut Bencher) {
|
||||
let mut output = vec![0; self.expected.len()];
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Benchmarking of calling builtin contract
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use account_state::State;
|
||||
use parity_bytes::Bytes;
|
||||
use ethcore::test_helpers::get_temp_state_db;
|
||||
use ethereum_types::{H160, U256};
|
||||
use criterion::{black_box, criterion_main, criterion_group, Criterion};
|
||||
use machine::{test_helpers, Machine};
|
||||
use machine::executive::CallCreateExecutive;
|
||||
use machine::substate::Substate;
|
||||
use trace::{NoopTracer, NoopVMTracer};
|
||||
use trie_vm_factories::VmFactory;
|
||||
use vm::{ActionParams, EnvInfo, Schedule};
|
||||
|
||||
const ECRECOVER: &str = "0000000000000000000000000000000000000001";
|
||||
const SHA256: &str = "0000000000000000000000000000000000000002";
|
||||
const SIGNED_DATA: &str = "hash000000000001v000000000000002r000000000000003s000000000000004";
|
||||
|
||||
fn single_builtin_pricing() -> Machine {
|
||||
test_helpers::load_machine(include_bytes!("../../res/ethereum/builtin_one_activation_bench.json"))
|
||||
}
|
||||
|
||||
fn multiple_builtin_pricing() -> Machine {
|
||||
test_helpers::load_machine(include_bytes!("../../res/ethereum/builtin_multi_bench.json"))
|
||||
}
|
||||
|
||||
fn builtin_params(address: H160, execute: bool) -> ActionParams {
|
||||
let mut params = ActionParams::default();
|
||||
params.code_address = address;
|
||||
params.gas = u64::max_value().into();
|
||||
if execute {
|
||||
params.data = Some(SIGNED_DATA.bytes().collect::<Bytes>());
|
||||
}
|
||||
params
|
||||
}
|
||||
|
||||
fn single_activation(c: &mut Criterion) {
|
||||
let contract = H160::from_str(ECRECOVER).unwrap();
|
||||
let params = builtin_params(contract, false);
|
||||
|
||||
let env_info = EnvInfo::default();
|
||||
let machine = single_builtin_pricing();
|
||||
let schedule = Schedule::default();
|
||||
let factory = VmFactory::default();
|
||||
let depth = 0;
|
||||
let stack_depth = 0;
|
||||
let parent_static_flag = false;
|
||||
|
||||
let db = get_temp_state_db();
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
let mut substate = Substate::new();
|
||||
|
||||
c.bench_function("single activation", move |b| {
|
||||
b.iter(|| black_box(CallCreateExecutive::new_call_raw(
|
||||
params.clone(),
|
||||
&env_info,
|
||||
&machine,
|
||||
&schedule,
|
||||
&factory,
|
||||
depth,
|
||||
stack_depth,
|
||||
parent_static_flag,
|
||||
).exec(&mut state, &mut substate, &mut NoopTracer, &mut NoopVMTracer))
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn ten_multiple_activations(c: &mut Criterion) {
|
||||
let contract = H160::from_str(ECRECOVER).unwrap();
|
||||
let params = builtin_params(contract, false);
|
||||
|
||||
let env_info = EnvInfo::default();
|
||||
let machine = multiple_builtin_pricing();
|
||||
let schedule = Schedule::default();
|
||||
let factory = VmFactory::default();
|
||||
let depth = 0;
|
||||
let stack_depth = 0;
|
||||
let parent_static_flag = false;
|
||||
|
||||
let db = get_temp_state_db();
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
let mut substate = Substate::new();
|
||||
|
||||
c.bench_function("ten activations", move |b| {
|
||||
b.iter(|| black_box(CallCreateExecutive::new_call_raw(
|
||||
params.clone(),
|
||||
&env_info,
|
||||
&machine,
|
||||
&schedule,
|
||||
&factory,
|
||||
depth,
|
||||
stack_depth,
|
||||
parent_static_flag,
|
||||
).exec(&mut state, &mut substate, &mut NoopTracer, &mut NoopVMTracer))
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn fourty_multiple_activations(c: &mut Criterion) {
|
||||
let contract = H160::from_str(SHA256).unwrap();
|
||||
let params = builtin_params(contract, false);
|
||||
|
||||
let env_info = EnvInfo::default();
|
||||
let machine = multiple_builtin_pricing();
|
||||
let schedule = Schedule::default();
|
||||
let factory = VmFactory::default();
|
||||
let depth = 0;
|
||||
let stack_depth = 0;
|
||||
let parent_static_flag = false;
|
||||
|
||||
let db = get_temp_state_db();
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
let mut substate = Substate::new();
|
||||
|
||||
c.bench_function("fourty activations", move |b| {
|
||||
b.iter(|| black_box(CallCreateExecutive::new_call_raw(
|
||||
params.clone(),
|
||||
&env_info,
|
||||
&machine,
|
||||
&schedule,
|
||||
&factory,
|
||||
depth,
|
||||
stack_depth,
|
||||
parent_static_flag,
|
||||
).exec(&mut state, &mut substate, &mut NoopTracer, &mut NoopVMTracer))
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, single_activation, ten_multiple_activations, fourty_multiple_activations);
|
||||
criterion_main!(benches);
|
||||
@@ -8,28 +8,26 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.11"
|
||||
ansi_term = "0.10"
|
||||
blooms-db = { path = "../../util/blooms-db" }
|
||||
common-types = { path = "../types" }
|
||||
ethcore-db = { path = "../db" }
|
||||
ethereum-types = "0.6.0"
|
||||
keccak-hash = "0.2.0"
|
||||
parity-util-mem = "0.1"
|
||||
ethereum-types = "0.4"
|
||||
heapsize = "0.4"
|
||||
itertools = "0.5"
|
||||
kvdb = "0.1"
|
||||
log = "0.4"
|
||||
parity-bytes = "0.1"
|
||||
parking_lot = "0.9"
|
||||
rand = "0.6"
|
||||
rayon = "1.0"
|
||||
rlp = "0.4.0"
|
||||
parking_lot = "0.7"
|
||||
rayon = "1.1"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_compress = { path = "../../util/rlp-compress" }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
keccak-hash = "0.1"
|
||||
rustc-hex = "1.0"
|
||||
tempdir = "0.3"
|
||||
kvdb-memorydb = "0.1"
|
||||
|
||||
@@ -24,8 +24,7 @@ use common_types::header::Header;
|
||||
/// For GHOST fork-choice rule it would typically describe the block with highest
|
||||
/// combined difficulty (usually the block with the highest block number).
|
||||
///
|
||||
/// Sometimes referred as 'latest block'.
|
||||
#[derive(Debug)]
|
||||
/// Sometimes refered as 'latest block'.
|
||||
pub struct BestBlock {
|
||||
/// Best block decoded header.
|
||||
pub header: Header,
|
||||
@@ -36,7 +35,7 @@ pub struct BestBlock {
|
||||
}
|
||||
|
||||
/// Best ancient block info. If the blockchain has a gap this keeps track of where it starts.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Default)]
|
||||
pub struct BestAncientBlock {
|
||||
/// Best block hash.
|
||||
pub hash: H256,
|
||||
|
||||
@@ -39,10 +39,10 @@ use ethcore_db::cache_manager::CacheManager;
|
||||
use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions};
|
||||
use ethcore_db::{self as db, Writable, Readable, CacheUpdatePolicy};
|
||||
use ethereum_types::{H256, Bloom, BloomRef, U256};
|
||||
use util_mem::{MallocSizeOf, allocators::new_malloc_size_ops};
|
||||
use heapsize::HeapSizeOf;
|
||||
use itertools::Itertools;
|
||||
use kvdb::{DBTransaction, KeyValueDB};
|
||||
use log::{trace, debug, warn, info};
|
||||
use log::{trace, warn, info};
|
||||
use parity_bytes::Bytes;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use rayon::prelude::*;
|
||||
@@ -57,7 +57,7 @@ use crate::{CacheSize, ImportRoute, Config};
|
||||
/// Database backing `BlockChain`.
|
||||
pub trait BlockChainDB: Send + Sync {
|
||||
/// Generic key value store.
|
||||
fn key_value(&self) -> &Arc<dyn KeyValueDB>;
|
||||
fn key_value(&self) -> &Arc<KeyValueDB>;
|
||||
|
||||
/// Header blooms database.
|
||||
fn blooms(&self) -> &blooms_db::Database;
|
||||
@@ -85,7 +85,7 @@ pub trait BlockChainDB: Send + Sync {
|
||||
/// predefined config.
|
||||
pub trait BlockChainDBHandler: Send + Sync {
|
||||
/// Open the predefined key-value database.
|
||||
fn open(&self, path: &Path) -> io::Result<Arc<dyn BlockChainDB>>;
|
||||
fn open(&self, path: &Path) -> io::Result<Arc<BlockChainDB>>;
|
||||
}
|
||||
|
||||
/// Interface for querying blocks by hash and by number.
|
||||
@@ -195,12 +195,6 @@ pub trait BlockProvider {
|
||||
where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized;
|
||||
}
|
||||
|
||||
/// Interface for querying blocks with pending db transaction by hash and by number.
|
||||
trait InTransactionBlockProvider {
|
||||
/// Get the familial details concerning a block.
|
||||
fn uncommitted_block_details(&self, hash: &H256) -> Option<BlockDetails>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
||||
enum CacheId {
|
||||
BlockHeader(H256),
|
||||
@@ -234,7 +228,7 @@ pub struct BlockChain {
|
||||
transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
|
||||
block_receipts: RwLock<HashMap<H256, BlockReceipts>>,
|
||||
|
||||
db: Arc<dyn BlockChainDB>,
|
||||
db: Arc<BlockChainDB>,
|
||||
|
||||
cache_man: Mutex<CacheManager<CacheId>>,
|
||||
|
||||
@@ -290,7 +284,7 @@ impl BlockProvider for BlockChain {
|
||||
}
|
||||
|
||||
// Read from DB and populate cache
|
||||
let b = self.db.key_value().get(db::COL_HEADERS, hash.as_bytes())
|
||||
let b = self.db.key_value().get(db::COL_HEADERS, hash)
|
||||
.expect("Low level database error when fetching block header data. Some issue with disk?")?;
|
||||
|
||||
let header = encoded::Header::new(decompress(&b, blocks_swapper()).into_vec());
|
||||
@@ -320,7 +314,7 @@ impl BlockProvider for BlockChain {
|
||||
}
|
||||
|
||||
// Read from DB and populate cache
|
||||
let b = self.db.key_value().get(db::COL_BODIES, hash.as_bytes())
|
||||
let b = self.db.key_value().get(db::COL_BODIES, hash)
|
||||
.expect("Low level database error when fetching block body data. Some issue with disk?")?;
|
||||
|
||||
let body = encoded::Body::new(decompress(&b, blocks_swapper()).into_vec());
|
||||
@@ -430,19 +424,6 @@ impl BlockProvider for BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
impl InTransactionBlockProvider for BlockChain {
|
||||
fn uncommitted_block_details(&self, hash: &H256) -> Option<BlockDetails> {
|
||||
let result = self.db.key_value().read_with_two_layer_cache(
|
||||
db::COL_EXTRA,
|
||||
&self.pending_block_details,
|
||||
&self.block_details,
|
||||
hash
|
||||
)?;
|
||||
self.cache_man.lock().note_used(CacheId::BlockDetails(*hash));
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator which walks the blockchain towards the genesis.
|
||||
#[derive(Clone)]
|
||||
pub struct AncestryIter<'a> {
|
||||
@@ -488,7 +469,7 @@ impl<'a> Iterator for AncestryWithMetadataIter<'a> {
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
self.current = H256::zero();
|
||||
self.current = H256::default();
|
||||
None
|
||||
},
|
||||
}
|
||||
@@ -500,7 +481,7 @@ impl<'a> Iterator for AncestryWithMetadataIter<'a> {
|
||||
/// Returns epoch transitions.
|
||||
pub struct EpochTransitionIter<'a> {
|
||||
chain: &'a BlockChain,
|
||||
prefix_iter: Box<dyn Iterator<Item=(Box<[u8]>, Box<[u8]>)> + 'a>,
|
||||
prefix_iter: Box<Iterator<Item=(Box<[u8]>, Box<[u8]>)> + 'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for EpochTransitionIter<'a> {
|
||||
@@ -540,7 +521,7 @@ impl<'a> Iterator for EpochTransitionIter<'a> {
|
||||
|
||||
impl BlockChain {
|
||||
/// Create new instance of blockchain from given Genesis.
|
||||
pub fn new(config: Config, genesis: &[u8], db: Arc<dyn BlockChainDB>) -> BlockChain {
|
||||
pub fn new(config: Config, genesis: &[u8], db: Arc<BlockChainDB>) -> BlockChain {
|
||||
// 400 is the average size of the key
|
||||
let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400);
|
||||
|
||||
@@ -591,13 +572,13 @@ impl BlockChain {
|
||||
};
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
batch.put(db::COL_HEADERS, hash.as_bytes(), block.header_rlp().as_raw());
|
||||
batch.put(db::COL_BODIES, hash.as_bytes(), &Self::block_to_body(genesis));
|
||||
batch.put(db::COL_HEADERS, &hash, block.header_rlp().as_raw());
|
||||
batch.put(db::COL_BODIES, &hash, &Self::block_to_body(genesis));
|
||||
|
||||
batch.write(db::COL_EXTRA, &hash, &details);
|
||||
batch.write(db::COL_EXTRA, &header.number(), &hash);
|
||||
|
||||
batch.put(db::COL_EXTRA, b"best", hash.as_bytes());
|
||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||
bc.db.key_value().write(batch).expect("Low level database error when fetching 'best' block. Some issue with disk?");
|
||||
hash
|
||||
}
|
||||
@@ -611,7 +592,7 @@ impl BlockChain {
|
||||
let best_block_rlp = bc.block(&best_block_hash)
|
||||
.expect("Best block is from a known block hash; qed");
|
||||
|
||||
// and write them to the cache.
|
||||
// and write them
|
||||
let mut best_block = bc.best_block.write();
|
||||
*best_block = BestBlock {
|
||||
total_difficulty: best_block_total_difficulty,
|
||||
@@ -658,7 +639,7 @@ impl BlockChain {
|
||||
if hash != bc.genesis_hash() {
|
||||
trace!("First block calculated: {:?}", hash);
|
||||
let mut batch = db.key_value().transaction();
|
||||
batch.put(db::COL_EXTRA, b"first", hash.as_bytes());
|
||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||
db.key_value().write(batch).expect("Low level database error when writing 'first' block. Some issue with disk?");
|
||||
bc.first_block = Some(hash);
|
||||
}
|
||||
@@ -671,7 +652,10 @@ impl BlockChain {
|
||||
// and write them
|
||||
if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) {
|
||||
let mut best_ancient_block = bc.best_ancient_block.write();
|
||||
*best_ancient_block = Some(BestAncientBlock { hash, number });
|
||||
*best_ancient_block = Some(BestAncientBlock {
|
||||
hash: hash,
|
||||
number: number,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,10 +713,6 @@ impl BlockChain {
|
||||
///
|
||||
/// If the tree route verges into pruned or unknown blocks,
|
||||
/// `None` is returned.
|
||||
///
|
||||
/// `is_from_route_finalized` returns whether the `from` part of the
|
||||
/// route contains a finalized block. This only holds if the two parts (from
|
||||
/// and to) are on different branches, ie. on 2 different forks.
|
||||
pub fn tree_route(&self, from: H256, to: H256) -> Option<TreeRoute> {
|
||||
let mut from_branch = vec![];
|
||||
let mut is_from_route_finalized = false;
|
||||
@@ -746,9 +726,9 @@ impl BlockChain {
|
||||
// reset from && to to the same level
|
||||
while from_details.number > to_details.number {
|
||||
from_branch.push(current_from);
|
||||
is_from_route_finalized = is_from_route_finalized || from_details.is_finalized;
|
||||
current_from = from_details.parent.clone();
|
||||
from_details = self.block_details(&from_details.parent)?;
|
||||
is_from_route_finalized = is_from_route_finalized || from_details.is_finalized;
|
||||
}
|
||||
|
||||
while to_details.number > from_details.number {
|
||||
@@ -762,9 +742,9 @@ impl BlockChain {
|
||||
// move to shared parent
|
||||
while current_from != current_to {
|
||||
from_branch.push(current_from);
|
||||
is_from_route_finalized = is_from_route_finalized || from_details.is_finalized;
|
||||
current_from = from_details.parent.clone();
|
||||
from_details = self.block_details(&from_details.parent)?;
|
||||
is_from_route_finalized = is_from_route_finalized || from_details.is_finalized;
|
||||
|
||||
to_branch.push(current_to);
|
||||
current_to = to_details.parent.clone();
|
||||
@@ -778,8 +758,8 @@ impl BlockChain {
|
||||
Some(TreeRoute {
|
||||
blocks: from_branch,
|
||||
ancestor: current_from,
|
||||
index,
|
||||
is_from_route_finalized,
|
||||
index: index,
|
||||
is_from_route_finalized: is_from_route_finalized,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -808,10 +788,10 @@ impl BlockChain {
|
||||
let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper());
|
||||
|
||||
// store block in db
|
||||
batch.put(db::COL_HEADERS, hash.as_bytes(), &compressed_header);
|
||||
batch.put(db::COL_BODIES, hash.as_bytes(), &compressed_body);
|
||||
batch.put(db::COL_HEADERS, &hash, &compressed_header);
|
||||
batch.put(db::COL_BODIES, &hash, &compressed_body);
|
||||
|
||||
let maybe_parent = self.uncommitted_block_details(&block_parent_hash);
|
||||
let maybe_parent = self.block_details(&block_parent_hash);
|
||||
|
||||
if let Some(parent_details) = maybe_parent {
|
||||
// parent known to be in chain.
|
||||
@@ -874,31 +854,12 @@ impl BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
/// clears all caches, re-loads best block from disk for testing purposes
|
||||
/// clears all caches for testing purposes
|
||||
pub fn clear_cache(&self) {
|
||||
self.block_bodies.write().clear();
|
||||
self.block_details.write().clear();
|
||||
self.block_hashes.write().clear();
|
||||
self.block_headers.write().clear();
|
||||
// Fetch best block details from disk
|
||||
let best_block_hash = self.db.key_value().get(db::COL_EXTRA, b"best")
|
||||
.expect("Low-level database error when fetching 'best' block. Some issue with disk?")
|
||||
.as_ref()
|
||||
.map(|r| H256::from_slice(r))
|
||||
.unwrap();
|
||||
let best_block_total_difficulty = self.block_details(&best_block_hash)
|
||||
.expect("Best block is from a known block hash; a known block hash always comes with a known block detail; qed")
|
||||
.total_difficulty;
|
||||
let best_block_rlp = self.block(&best_block_hash)
|
||||
.expect("Best block is from a known block hash; qed");
|
||||
|
||||
// and write them to the cache
|
||||
let mut best_block = self.best_block.write();
|
||||
*best_block = BestBlock {
|
||||
total_difficulty: best_block_total_difficulty,
|
||||
header: best_block_rlp.decode_header(),
|
||||
block: best_block_rlp,
|
||||
};
|
||||
}
|
||||
|
||||
/// Update the best ancient block to the given hash, after checking that
|
||||
@@ -971,7 +932,7 @@ impl BlockChain {
|
||||
*pending_best_ancient_block = Some(None);
|
||||
} else if block_number > ancient_number {
|
||||
trace!(target: "blockchain", "Updating the best ancient block to {}.", block_number);
|
||||
batch.put(db::COL_EXTRA, b"ancient", block_hash.as_bytes());
|
||||
batch.put(db::COL_EXTRA, b"ancient", &block_hash);
|
||||
*pending_best_ancient_block = Some(Some(BestAncientBlock {
|
||||
hash: *block_hash,
|
||||
number: block_number,
|
||||
@@ -1002,7 +963,6 @@ impl BlockChain {
|
||||
/// Iterate over all epoch transitions.
|
||||
/// This will only return transitions within the canonical chain.
|
||||
pub fn epoch_transitions(&self) -> EpochTransitionIter {
|
||||
debug!(target: "blockchain", "Iterating over all epoch transitions");
|
||||
let iter = self.db.key_value().iter_from_prefix(db::COL_EXTRA, &EPOCH_KEY_PREFIX[..]);
|
||||
EpochTransitionIter {
|
||||
chain: self,
|
||||
@@ -1028,9 +988,7 @@ impl BlockChain {
|
||||
pub fn epoch_transition_for(&self, parent_hash: H256) -> Option<EpochTransition> {
|
||||
// slow path: loop back block by block
|
||||
for hash in self.ancestry_iter(parent_hash)? {
|
||||
trace!(target: "blockchain", "Got parent hash {} from ancestry_iter", hash);
|
||||
let details = self.block_details(&hash)?;
|
||||
trace!(target: "blockchain", "Block #{}: Got block details for parent hash {}", details.number, hash);
|
||||
|
||||
// look for transition in database.
|
||||
if let Some(transition) = self.epoch_transition(details.number, hash) {
|
||||
@@ -1042,22 +1000,11 @@ impl BlockChain {
|
||||
//
|
||||
// if `block_hash` is canonical it will only return transitions up to
|
||||
// the parent.
|
||||
match self.block_hash(details.number) {
|
||||
Some(h) if h == hash => {
|
||||
return self.epoch_transitions()
|
||||
.map(|(_, t)| t)
|
||||
.take_while(|t| t.block_number <= details.number)
|
||||
.last()
|
||||
},
|
||||
Some(h) => {
|
||||
warn!(target: "blockchain", "Block #{}: Found non-canonical block hash {} (expected {})", details.number, h, hash);
|
||||
|
||||
trace!(target: "blockchain", "Block #{} Mismatched hashes. Ancestor {} != Own {}", details.number, hash, h);
|
||||
trace!(target: "blockchain", " Ancestor {}: #{:#?}", hash, details);
|
||||
trace!(target: "blockchain", " Own {}: #{:#?}", h, self.block_details(&h));
|
||||
|
||||
},
|
||||
None => trace!(target: "blockchain", "Block #{}: hash {} not found in cache or DB", details.number, hash),
|
||||
if self.block_hash(details.number)? == hash {
|
||||
return self.epoch_transitions()
|
||||
.map(|(_, t)| t)
|
||||
.take_while(|t| t.block_number <= details.number)
|
||||
.last()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,7 +1029,7 @@ impl BlockChain {
|
||||
///
|
||||
/// Used in snapshots to glue the chunks together at the end.
|
||||
pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) {
|
||||
let mut parent_details = self.uncommitted_block_details(&block_hash)
|
||||
let mut parent_details = self.block_details(&block_hash)
|
||||
.unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash));
|
||||
|
||||
parent_details.children.push(child_hash);
|
||||
@@ -1125,8 +1072,8 @@ impl BlockChain {
|
||||
let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper());
|
||||
|
||||
// store block in db
|
||||
batch.put(db::COL_HEADERS, hash.as_bytes(), &compressed_header);
|
||||
batch.put(db::COL_BODIES, hash.as_bytes(), &compressed_body);
|
||||
batch.put(db::COL_HEADERS, &hash, &compressed_header);
|
||||
batch.put(db::COL_BODIES, &hash, &compressed_body);
|
||||
|
||||
let info = self.block_info(&block.header_view(), route, &extras);
|
||||
|
||||
@@ -1189,7 +1136,7 @@ impl BlockChain {
|
||||
/// Mark a block to be considered finalized. Returns `Some(())` if the operation succeeds, and `None` if the block
|
||||
/// hash is not found.
|
||||
pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Option<()> {
|
||||
let mut block_details = self.uncommitted_block_details(&block_hash)?;
|
||||
let mut block_details = self.block_details(&block_hash)?;
|
||||
block_details.is_finalized = true;
|
||||
|
||||
self.update_block_details(batch, block_hash, block_details);
|
||||
@@ -1225,7 +1172,7 @@ impl BlockChain {
|
||||
{
|
||||
let mut best_block = self.pending_best_block.write();
|
||||
if is_best && update.info.location != BlockLocation::Branch {
|
||||
batch.put(db::COL_EXTRA, b"best", update.info.hash.as_bytes());
|
||||
batch.put(db::COL_EXTRA, b"best", &update.info.hash);
|
||||
*best_block = Some(BestBlock {
|
||||
total_difficulty: update.info.total_difficulty,
|
||||
header: update.block.decode_header(),
|
||||
@@ -1312,7 +1259,7 @@ impl BlockChain {
|
||||
current: if self.is_known(&first) {
|
||||
first
|
||||
} else {
|
||||
H256::zero() // zero hash
|
||||
H256::default() // zero hash
|
||||
},
|
||||
chain: self
|
||||
}
|
||||
@@ -1382,7 +1329,7 @@ impl BlockChain {
|
||||
/// Uses the given parent details or attempts to load them from the database.
|
||||
fn prepare_block_details_update(&self, parent_hash: H256, info: &BlockInfo, is_finalized: bool) -> HashMap<H256, BlockDetails> {
|
||||
// update parent
|
||||
let mut parent_details = self.uncommitted_block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
let mut parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
parent_details.children.push(info.hash);
|
||||
|
||||
// create current block details.
|
||||
@@ -1528,12 +1475,11 @@ impl BlockChain {
|
||||
|
||||
/// Get current cache size.
|
||||
pub fn cache_size(&self) -> CacheSize {
|
||||
let mut ops = new_malloc_size_ops();
|
||||
CacheSize {
|
||||
blocks: self.block_headers.size_of(&mut ops) + self.block_bodies.size_of(&mut ops),
|
||||
block_details: self.block_details.size_of(&mut ops),
|
||||
transaction_addresses: self.transaction_addresses.size_of(&mut ops),
|
||||
block_receipts: self.block_receipts.size_of(&mut ops),
|
||||
blocks: self.block_headers.read().heap_size_of_children() + self.block_bodies.read().heap_size_of_children(),
|
||||
block_details: self.block_details.read().heap_size_of_children(),
|
||||
transaction_addresses: self.transaction_addresses.read().heap_size_of_children(),
|
||||
block_receipts: self.block_receipts.read().heap_size_of_children(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1568,13 +1514,12 @@ impl BlockChain {
|
||||
transaction_addresses.shrink_to_fit();
|
||||
block_receipts.shrink_to_fit();
|
||||
|
||||
let mut ops = new_malloc_size_ops();
|
||||
block_headers.size_of(&mut ops) +
|
||||
block_bodies.size_of(&mut ops) +
|
||||
block_details.size_of(&mut ops) +
|
||||
block_hashes.size_of(&mut ops) +
|
||||
transaction_addresses.size_of(&mut ops) +
|
||||
block_receipts.size_of(&mut ops)
|
||||
block_headers.heap_size_of_children() +
|
||||
block_bodies.heap_size_of_children() +
|
||||
block_details.heap_size_of_children() +
|
||||
block_hashes.heap_size_of_children() +
|
||||
transaction_addresses.heap_size_of_children() +
|
||||
block_receipts.heap_size_of_children()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1626,18 +1571,17 @@ mod tests {
|
||||
use keccak_hash::keccak;
|
||||
use rustc_hex::FromHex;
|
||||
use tempdir::TempDir;
|
||||
use std::str::FromStr;
|
||||
|
||||
struct TestBlockChainDB {
|
||||
_blooms_dir: TempDir,
|
||||
_trace_blooms_dir: TempDir,
|
||||
blooms: blooms_db::Database,
|
||||
trace_blooms: blooms_db::Database,
|
||||
key_value: Arc<dyn KeyValueDB>,
|
||||
key_value: Arc<KeyValueDB>,
|
||||
}
|
||||
|
||||
impl BlockChainDB for TestBlockChainDB {
|
||||
fn key_value(&self) -> &Arc<dyn KeyValueDB> {
|
||||
fn key_value(&self) -> &Arc<KeyValueDB> {
|
||||
&self.key_value
|
||||
}
|
||||
|
||||
@@ -1651,7 +1595,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Creates new test instance of `BlockChainDB`
|
||||
pub fn new_db() -> Arc<dyn BlockChainDB> {
|
||||
pub fn new_db() -> Arc<BlockChainDB> {
|
||||
let blooms_dir = TempDir::new("").unwrap();
|
||||
let trace_blooms_dir = TempDir::new("").unwrap();
|
||||
|
||||
@@ -1666,15 +1610,15 @@ mod tests {
|
||||
Arc::new(db)
|
||||
}
|
||||
|
||||
fn new_chain(genesis: encoded::Block, db: Arc<dyn BlockChainDB>) -> BlockChain {
|
||||
fn new_chain(genesis: encoded::Block, db: Arc<BlockChainDB>) -> BlockChain {
|
||||
BlockChain::new(Config::default(), genesis.raw(), db)
|
||||
}
|
||||
|
||||
fn insert_block(db: &Arc<dyn BlockChainDB>, bc: &BlockChain, block: encoded::Block, receipts: Vec<Receipt>) -> ImportRoute {
|
||||
fn insert_block(db: &Arc<BlockChainDB>, bc: &BlockChain, block: encoded::Block, receipts: Vec<Receipt>) -> ImportRoute {
|
||||
insert_block_commit(db, bc, block, receipts, true)
|
||||
}
|
||||
|
||||
fn insert_block_commit(db: &Arc<dyn BlockChainDB>, bc: &BlockChain, block: encoded::Block, receipts: Vec<Receipt>, commit: bool) -> ImportRoute {
|
||||
fn insert_block_commit(db: &Arc<BlockChainDB>, bc: &BlockChain, block: encoded::Block, receipts: Vec<Receipt>, commit: bool) -> ImportRoute {
|
||||
let mut batch = db.key_value().transaction();
|
||||
let res = insert_block_batch(&mut batch, bc, block, receipts);
|
||||
db.key_value().write(batch).unwrap();
|
||||
@@ -1685,10 +1629,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn insert_block_batch(batch: &mut DBTransaction, bc: &BlockChain, block: encoded::Block, receipts: Vec<Receipt>) -> ImportRoute {
|
||||
use crate::ExtrasInsert;
|
||||
|
||||
let fork_choice = {
|
||||
let header = block.header_view();
|
||||
let parent_hash = header.parent_hash();
|
||||
let parent_details = bc.uncommitted_block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
let parent_details = bc.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
let block_total_difficulty = parent_details.total_difficulty + header.difficulty();
|
||||
if block_total_difficulty > bc.best_block_total_difficulty() {
|
||||
common_types::engines::ForkChoice::New
|
||||
@@ -2111,7 +2057,7 @@ mod tests {
|
||||
fn find_transaction_by_hash() {
|
||||
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap();
|
||||
let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap();
|
||||
let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap();
|
||||
let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into();
|
||||
|
||||
let db = new_db();
|
||||
let bc = new_chain(encoded::Block::new(genesis), db.clone());
|
||||
@@ -2184,7 +2130,7 @@ mod tests {
|
||||
let db = new_db();
|
||||
let bc = new_chain(genesis.last().encoded(), db.clone());
|
||||
insert_block(&db, &bc, b1.last().encoded(), vec![Receipt {
|
||||
outcome: TransactionOutcome::StateRoot(H256::zero()),
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
@@ -2193,7 +2139,7 @@ mod tests {
|
||||
],
|
||||
},
|
||||
Receipt {
|
||||
outcome: TransactionOutcome::StateRoot(H256::zero()),
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
@@ -2202,7 +2148,7 @@ mod tests {
|
||||
}]);
|
||||
insert_block(&db, &bc, b2.last().encoded(), vec![
|
||||
Receipt {
|
||||
outcome: TransactionOutcome::StateRoot(H256::zero()),
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
@@ -2212,7 +2158,7 @@ mod tests {
|
||||
]);
|
||||
insert_block(&db, &bc, b3.last().encoded(), vec![
|
||||
Receipt {
|
||||
outcome: TransactionOutcome::StateRoot(H256::zero()),
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
@@ -2291,11 +2237,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_bloom_filter_simple() {
|
||||
let bloom_b1 = Bloom::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap();
|
||||
let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into();
|
||||
|
||||
let bloom_b2 = Bloom::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||
|
||||
let bloom_ba = Bloom::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let bloom_ba: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||
|
||||
let genesis = BlockBuilder::genesis();
|
||||
let b1 = genesis.add_block_with(|| BlockOptions {
|
||||
@@ -2359,11 +2305,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_insert_unordered() {
|
||||
let bloom_b1 = Bloom::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap();
|
||||
let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into();
|
||||
|
||||
let bloom_b2 = Bloom::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||
|
||||
let bloom_b3 = Bloom::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let bloom_b3: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||
|
||||
let genesis = BlockBuilder::genesis();
|
||||
let b1 = genesis.add_block_with_bloom(bloom_b1);
|
||||
@@ -2545,74 +2491,4 @@ mod tests {
|
||||
assert_eq!(bc.epoch_transition_for(fork_hash).unwrap().block_number, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_rout_with_finalization() {
|
||||
let genesis = BlockBuilder::genesis();
|
||||
let a = genesis.add_block();
|
||||
// First branch
|
||||
let a1 = a.add_block_with_random_transactions();
|
||||
let a2 = a1.add_block_with_random_transactions();
|
||||
let a3 = a2.add_block_with_random_transactions();
|
||||
// Second branch
|
||||
let b1 = a.add_block_with_random_transactions();
|
||||
let b2 = b1.add_block_with_random_transactions();
|
||||
|
||||
let a_hash = a.last().hash();
|
||||
let a1_hash = a1.last().hash();
|
||||
let a2_hash = a2.last().hash();
|
||||
let a3_hash = a3.last().hash();
|
||||
let b2_hash = b2.last().hash();
|
||||
|
||||
let bootstrap_chain = |blocks: Vec<&BlockBuilder>| {
|
||||
let db = new_db();
|
||||
let bc = new_chain(genesis.last().encoded(), db.clone());
|
||||
let mut batch = db.key_value().transaction();
|
||||
for block in blocks {
|
||||
insert_block_batch(&mut batch, &bc, block.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
db.key_value().write(batch).unwrap();
|
||||
(db, bc)
|
||||
};
|
||||
|
||||
let mark_finalized = |block_hash: H256, db: &Arc<dyn BlockChainDB>, bc: &BlockChain| {
|
||||
let mut batch = db.key_value().transaction();
|
||||
bc.mark_finalized(&mut batch, block_hash).unwrap();
|
||||
bc.commit();
|
||||
db.key_value().write(batch).unwrap();
|
||||
};
|
||||
|
||||
// Case 1: fork, with finalized common ancestor
|
||||
{
|
||||
let (db, bc) = bootstrap_chain(vec![&a, &a1, &a2, &a3, &b1, &b2]);
|
||||
assert_eq!(bc.best_block_hash(), a3_hash);
|
||||
assert_eq!(bc.block_hash(2).unwrap(), a1_hash);
|
||||
|
||||
mark_finalized(a_hash, &db, &bc);
|
||||
assert!(!bc.tree_route(a3_hash, b2_hash).unwrap().is_from_route_finalized);
|
||||
assert!(!bc.tree_route(b2_hash, a3_hash).unwrap().is_from_route_finalized);
|
||||
}
|
||||
|
||||
// Case 2: fork with a finalized block on a branch
|
||||
{
|
||||
let (db, bc) = bootstrap_chain(vec![&a, &a1, &a2, &a3, &b1, &b2]);
|
||||
assert_eq!(bc.best_block_hash(), a3_hash);
|
||||
assert_eq!(bc.block_hash(2).unwrap(), a1_hash);
|
||||
|
||||
mark_finalized(a2_hash, &db, &bc);
|
||||
assert!(bc.tree_route(a3_hash, b2_hash).unwrap().is_from_route_finalized);
|
||||
assert!(!bc.tree_route(b2_hash, a3_hash).unwrap().is_from_route_finalized);
|
||||
}
|
||||
|
||||
// Case 3: no-fork, with a finalized block
|
||||
{
|
||||
let (db, bc) = bootstrap_chain(vec![&a, &a1, &a2]);
|
||||
assert_eq!(bc.best_block_hash(), a2_hash);
|
||||
|
||||
mark_finalized(a1_hash, &db, &bc);
|
||||
assert!(!bc.tree_route(a1_hash, a2_hash).unwrap().is_from_route_finalized);
|
||||
assert!(!bc.tree_route(a2_hash, a1_hash).unwrap().is_from_route_finalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,11 @@ use ethereum_types::{U256, H256, Bloom};
|
||||
|
||||
use common_types::encoded;
|
||||
use common_types::header::Header;
|
||||
use common_types::transaction::{SignedTransaction, Transaction, Action};
|
||||
use common_types::transaction::SignedTransaction;
|
||||
use common_types::view;
|
||||
use common_types::views::BlockView;
|
||||
use keccak_hash::keccak;
|
||||
use rlp::encode;
|
||||
use rlp_derive::RlpEncodable;
|
||||
use triehash_ethereum::ordered_trie_root;
|
||||
|
||||
/// Helper structure, used for encoding blocks.
|
||||
#[derive(Default, Clone, RlpEncodable)]
|
||||
@@ -138,29 +136,6 @@ impl BlockBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a block with randomly generated transactions.
|
||||
#[inline]
|
||||
pub fn add_block_with_random_transactions(&self) -> Self {
|
||||
// Maximum of ~50 transactions
|
||||
let count = rand::random::<u8>() as usize / 5;
|
||||
let transactions = std::iter::repeat_with(|| {
|
||||
let data_len = rand::random::<u8>();
|
||||
let data = std::iter::repeat_with(|| rand::random::<u8>())
|
||||
.take(data_len as usize)
|
||||
.collect::<Vec<_>>();
|
||||
Transaction {
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data,
|
||||
}.sign(&keccak("").into(), None)
|
||||
}).take(count);
|
||||
|
||||
self.add_block_with_transactions(transactions)
|
||||
}
|
||||
|
||||
/// Add a block with given transactions.
|
||||
#[inline]
|
||||
pub fn add_block_with_transactions<T>(&self, transactions: T) -> Self
|
||||
@@ -191,15 +166,11 @@ impl BlockBuilder {
|
||||
let mut block = Block::default();
|
||||
let metadata = get_metadata();
|
||||
let block_number = parent_number + 1;
|
||||
let transactions = metadata.transactions;
|
||||
let transactions_root = ordered_trie_root(transactions.iter().map(rlp::encode));
|
||||
|
||||
block.header.set_parent_hash(parent_hash);
|
||||
block.header.set_number(block_number);
|
||||
block.header.set_log_bloom(metadata.bloom);
|
||||
block.header.set_difficulty(metadata.difficulty);
|
||||
block.header.set_transactions_root(transactions_root);
|
||||
block.transactions = transactions;
|
||||
block.transactions = metadata.transactions;
|
||||
|
||||
parent_hash = block.hash();
|
||||
parent_number = block_number;
|
||||
|
||||
@@ -68,7 +68,7 @@ impl From<BlockInfo> for ImportRoute {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethereum_types::{U256, BigEndianHash};
|
||||
use ethereum_types::{H256, U256};
|
||||
use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
||||
use super::ImportRoute;
|
||||
|
||||
@@ -84,7 +84,7 @@ mod tests {
|
||||
#[test]
|
||||
fn import_route_branch() {
|
||||
let info = BlockInfo {
|
||||
hash: BigEndianHash::from_uint(&U256::from(1)),
|
||||
hash: H256::from(U256::from(1)),
|
||||
number: 0,
|
||||
total_difficulty: U256::from(0),
|
||||
location: BlockLocation::Branch,
|
||||
@@ -93,14 +93,14 @@ mod tests {
|
||||
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||
retracted: vec![],
|
||||
enacted: vec![],
|
||||
omitted: vec![BigEndianHash::from_uint(&U256::from(1))],
|
||||
omitted: vec![H256::from(U256::from(1))],
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_route_canon_chain() {
|
||||
let info = BlockInfo {
|
||||
hash: BigEndianHash::from_uint(&U256::from(1)),
|
||||
hash: H256::from(U256::from(1)),
|
||||
number: 0,
|
||||
total_difficulty: U256::from(0),
|
||||
location: BlockLocation::CanonChain,
|
||||
@@ -108,7 +108,7 @@ mod tests {
|
||||
|
||||
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||
retracted: vec![],
|
||||
enacted: vec![BigEndianHash::from_uint(&U256::from(1))],
|
||||
enacted: vec![H256::from(U256::from(1))],
|
||||
omitted: vec![],
|
||||
});
|
||||
}
|
||||
@@ -116,19 +116,19 @@ mod tests {
|
||||
#[test]
|
||||
fn import_route_branch_becoming_canon_chain() {
|
||||
let info = BlockInfo {
|
||||
hash: BigEndianHash::from_uint(&U256::from(2)),
|
||||
hash: H256::from(U256::from(2)),
|
||||
number: 0,
|
||||
total_difficulty: U256::from(0),
|
||||
location: BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData {
|
||||
ancestor: BigEndianHash::from_uint(&U256::from(0)),
|
||||
enacted: vec![BigEndianHash::from_uint(&U256::from(1))],
|
||||
retracted: vec![BigEndianHash::from_uint(&U256::from(3)), BigEndianHash::from_uint(&U256::from(4))],
|
||||
ancestor: H256::from(U256::from(0)),
|
||||
enacted: vec![H256::from(U256::from(1))],
|
||||
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
|
||||
})
|
||||
};
|
||||
|
||||
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||
retracted: vec![BigEndianHash::from_uint(&U256::from(3)), BigEndianHash::from_uint(&U256::from(4))],
|
||||
enacted: vec![BigEndianHash::from_uint(&U256::from(1)), BigEndianHash::from_uint(&U256::from(2))],
|
||||
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
|
||||
enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))],
|
||||
omitted: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate parity_util_mem as util_mem;
|
||||
extern crate parity_util_mem as malloc_size_of;
|
||||
|
||||
mod best_block;
|
||||
mod block_info;
|
||||
mod blockchain;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
[package]
|
||||
description = "ethereum vm builtin"
|
||||
name = "ethcore-builtin"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bn = { git = "https://github.com/paritytech/bn", default-features = false }
|
||||
ethereum-types = "0.6.0"
|
||||
byteorder = "1.3.2"
|
||||
common-types = { path = "../types" }
|
||||
eip-152 = { path = "../../util/EIP-152" }
|
||||
ethjson = { path = "../../json" }
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
keccak-hash = "0.2.0"
|
||||
log = "0.4"
|
||||
macros = { path = "../../util/macros" }
|
||||
num = { version = "0.1", default-features = false, features = ["bigint"] }
|
||||
parity-bytes = "0.1"
|
||||
parity-crypto = "0.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.2.1"
|
||||
parity-crypto = "0.4.0"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,5 +8,5 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
types = { path = "../types", package = "common-types" }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
bytes = { version = "0.1", package = "parity-bytes" }
|
||||
|
||||
@@ -9,9 +9,9 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common-types = { path = "../types" }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
heapsize = "0.4"
|
||||
kvdb = "0.1"
|
||||
parity-util-mem = "0.1"
|
||||
parking_lot = "0.9"
|
||||
rlp = "0.4.0"
|
||||
parking_lot = "0.7"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Database utilities and definitions.
|
||||
|
||||
use std::convert::AsRef;
|
||||
use std::ops::Deref;
|
||||
use std::hash::Hash;
|
||||
use std::collections::HashMap;
|
||||
use parking_lot::RwLock;
|
||||
@@ -82,7 +82,7 @@ impl<K, V> Cache<K, V> for HashMap<K, V> where K: Hash + Eq {
|
||||
/// Should be used to get database key associated with given value.
|
||||
pub trait Key<T> {
|
||||
/// The db key associated with this value.
|
||||
type Target: AsRef<[u8]>;
|
||||
type Target: Deref<Target = [u8]>;
|
||||
|
||||
/// Returns db key.
|
||||
fn key(&self) -> Self::Target;
|
||||
@@ -91,16 +91,16 @@ pub trait Key<T> {
|
||||
/// Should be used to write value into database.
|
||||
pub trait Writable {
|
||||
/// Writes the value into the database.
|
||||
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: rlp::Encodable, R: AsRef<[u8]>;
|
||||
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: rlp::Encodable, R: Deref<Target = [u8]>;
|
||||
|
||||
/// Deletes key from the databse.
|
||||
fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>) where T: rlp::Encodable, R: AsRef<[u8]>;
|
||||
fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>) where T: rlp::Encodable, R: Deref<Target = [u8]>;
|
||||
|
||||
/// Writes the value into the database and updates the cache.
|
||||
fn write_with_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: rlp::Encodable,
|
||||
R: AsRef<[u8]> {
|
||||
R: Deref<Target = [u8]> {
|
||||
self.write(col, &key, &value);
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
@@ -116,7 +116,7 @@ pub trait Writable {
|
||||
fn extend_with_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: rlp::Encodable,
|
||||
R: AsRef<[u8]> {
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values {
|
||||
@@ -137,7 +137,7 @@ pub trait Writable {
|
||||
fn extend_with_option_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, Option<T>>, values: HashMap<K, Option<T>>, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: rlp::Encodable,
|
||||
R: AsRef<[u8]> {
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values {
|
||||
@@ -167,7 +167,7 @@ pub trait Readable {
|
||||
/// Returns value for given key.
|
||||
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where
|
||||
T: rlp::Decodable,
|
||||
R: AsRef<[u8]>;
|
||||
R: Deref<Target = [u8]>;
|
||||
|
||||
/// Returns value for given key either in cache or in database.
|
||||
fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T> where
|
||||
@@ -188,28 +188,13 @@ pub trait Readable {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns value for given key either in two-layered cache or in database.
|
||||
fn read_with_two_layer_cache<K, T, C>(&self, col: Option<u32>, l1_cache: &RwLock<C>, l2_cache: &RwLock<C>, key: &K) -> Option<T> where
|
||||
K: Key<T> + Eq + Hash + Clone,
|
||||
T: Clone + rlp::Decodable,
|
||||
C: Cache<K, T> {
|
||||
{
|
||||
let read = l1_cache.read();
|
||||
if let Some(v) = read.get(key) {
|
||||
return Some(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.read_with_cache(col, l2_cache, key)
|
||||
}
|
||||
|
||||
/// Returns true if given value exists.
|
||||
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: AsRef<[u8]>;
|
||||
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>;
|
||||
|
||||
/// Returns true if given value exists either in cache or in database.
|
||||
fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool where
|
||||
K: Eq + Hash + Key<T, Target = R>,
|
||||
R: AsRef<[u8]>,
|
||||
R: Deref<Target = [u8]>,
|
||||
C: Cache<K, T> {
|
||||
{
|
||||
let read = cache.read();
|
||||
@@ -223,31 +208,31 @@ pub trait Readable {
|
||||
}
|
||||
|
||||
impl Writable for DBTransaction {
|
||||
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: rlp::Encodable, R: AsRef<[u8]> {
|
||||
self.put(col, key.key().as_ref(), &rlp::encode(value));
|
||||
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: rlp::Encodable, R: Deref<Target = [u8]> {
|
||||
self.put(col, &key.key(), &rlp::encode(value));
|
||||
}
|
||||
|
||||
fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>) where T: rlp::Encodable, R: AsRef<[u8]> {
|
||||
self.delete(col, key.key().as_ref());
|
||||
fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>) where T: rlp::Encodable, R: Deref<Target = [u8]> {
|
||||
self.delete(col, &key.key());
|
||||
}
|
||||
}
|
||||
|
||||
impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
|
||||
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
|
||||
where T: rlp::Decodable, R: AsRef<[u8]> {
|
||||
self.get(col, key.key().as_ref())
|
||||
.expect(&format!("db get failed, key: {:?}", key.key().as_ref()))
|
||||
where T: rlp::Decodable, R: Deref<Target = [u8]> {
|
||||
self.get(col, &key.key())
|
||||
.expect(&format!("db get failed, key: {:?}", &key.key() as &[u8]))
|
||||
.map(|v| rlp::decode(&v).expect("decode db value failed") )
|
||||
|
||||
}
|
||||
|
||||
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: AsRef<[u8]> {
|
||||
let result = self.get(col, key.key().as_ref());
|
||||
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> {
|
||||
let result = self.get(col, &key.key());
|
||||
|
||||
match result {
|
||||
Ok(v) => v.is_some(),
|
||||
Err(err) => {
|
||||
panic!("db get failed, key: {:?}, err: {:?}", key.key().as_ref(), err);
|
||||
panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
//! Blockchain DB extras.
|
||||
|
||||
use std::io::Write;
|
||||
use std::convert::AsRef;
|
||||
use std::ops;
|
||||
|
||||
use common_types::BlockNumber;
|
||||
use common_types::engines::epoch::Transition as EpochTransition;
|
||||
use common_types::receipt::Receipt;
|
||||
use ethereum_types::{H256, H264, U256};
|
||||
use parity_util_mem::MallocSizeOf;
|
||||
use heapsize::HeapSizeOf;
|
||||
use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
|
||||
use rlp;
|
||||
use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper, RlpEncodable, RlpDecodable};
|
||||
@@ -49,17 +49,19 @@ pub enum ExtrasIndex {
|
||||
|
||||
fn with_index(hash: &H256, i: ExtrasIndex) -> H264 {
|
||||
let mut result = H264::default();
|
||||
result.as_bytes_mut()[0] = i as u8;
|
||||
result.as_bytes_mut()[1..].clone_from_slice(hash.as_bytes());
|
||||
result[0] = i as u8;
|
||||
(*result)[1..].clone_from_slice(hash);
|
||||
result
|
||||
}
|
||||
|
||||
/// Wrapper for block number used as a DB key.
|
||||
pub struct BlockNumberKey([u8; 5]);
|
||||
|
||||
impl AsRef<[u8]> for BlockNumberKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0[..]
|
||||
impl ops::Deref for BlockNumberKey {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +123,10 @@ pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[
|
||||
/// Epoch transitions key
|
||||
pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]);
|
||||
|
||||
impl AsRef<[u8]> for EpochTransitionsKey {
|
||||
fn as_ref(&self) -> &[u8] { &self.0[..] }
|
||||
impl ops::Deref for EpochTransitionsKey {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] { &self.0[..] }
|
||||
}
|
||||
|
||||
impl Key<EpochTransitions> for u64 {
|
||||
@@ -140,7 +144,7 @@ impl Key<EpochTransitions> for u64 {
|
||||
}
|
||||
|
||||
/// Familial details concerning a block
|
||||
#[derive(Debug, Clone, MallocSizeOf)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockDetails {
|
||||
/// Block number
|
||||
pub number: BlockNumber,
|
||||
@@ -195,8 +199,14 @@ impl rlp::Decodable for BlockDetails {
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for BlockDetails {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.children.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents address of certain transaction within block
|
||||
#[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable, MallocSizeOf)]
|
||||
#[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable)]
|
||||
pub struct TransactionAddress {
|
||||
/// Block hash
|
||||
pub block_hash: H256,
|
||||
@@ -204,8 +214,12 @@ pub struct TransactionAddress {
|
||||
pub index: usize
|
||||
}
|
||||
|
||||
impl HeapSizeOf for TransactionAddress {
|
||||
fn heap_size_of_children(&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
/// Contains all block receipts.
|
||||
#[derive(Debug, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)]
|
||||
#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)]
|
||||
pub struct BlockReceipts {
|
||||
/// Block receipts
|
||||
pub receipts: Vec<Receipt>,
|
||||
@@ -214,7 +228,15 @@ pub struct BlockReceipts {
|
||||
impl BlockReceipts {
|
||||
/// Create new block receipts wrapper.
|
||||
pub fn new(receipts: Vec<Receipt>) -> Self {
|
||||
BlockReceipts { receipts }
|
||||
BlockReceipts {
|
||||
receipts: receipts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for BlockReceipts {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.receipts.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate parity_util_mem as mem;
|
||||
extern crate parity_util_mem as malloc_size_of;
|
||||
|
||||
mod db;
|
||||
|
||||
pub mod keys;
|
||||
|
||||
@@ -7,19 +7,19 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
bit-set = "0.4"
|
||||
parity-bytes = "0.1"
|
||||
ethereum-types = "0.6.0"
|
||||
parity-util-mem = "0.1"
|
||||
ethereum-types = "0.4"
|
||||
heapsize = "0.4"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
vm = { path = "../vm" }
|
||||
keccak-hash = "0.2.0"
|
||||
parking_lot = "0.9"
|
||||
keccak-hash = "0.1"
|
||||
parking_lot = "0.7"
|
||||
memory-cache = { path = "../../util/memory-cache" }
|
||||
num-bigint = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-hex = "1.0"
|
||||
criterion = "0.3"
|
||||
hex-literal = "0.2.0"
|
||||
criterion = "0.2"
|
||||
|
||||
[features]
|
||||
evm-debug = []
|
||||
|
||||
@@ -21,7 +21,7 @@ extern crate criterion;
|
||||
extern crate bit_set;
|
||||
extern crate ethereum_types;
|
||||
extern crate parking_lot;
|
||||
extern crate parity_util_mem as mem;
|
||||
extern crate heapsize;
|
||||
extern crate vm;
|
||||
extern crate evm;
|
||||
extern crate keccak_hash as hash;
|
||||
|
||||
@@ -44,18 +44,12 @@ pub trait Finalize {
|
||||
impl Finalize for Result<GasLeft> {
|
||||
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
|
||||
match self {
|
||||
Ok(GasLeft::Known(gas_left)) => {
|
||||
Ok(FinalizationResult {
|
||||
gas_left,
|
||||
apply_state: true,
|
||||
return_data: ReturnData::empty()
|
||||
})
|
||||
},
|
||||
Ok(GasLeft::NeedsReturn { gas_left, data, apply_state }) => {
|
||||
ext.ret(&gas_left, &data, apply_state).map(|gas_left|
|
||||
FinalizationResult { gas_left, apply_state, return_data: data }
|
||||
)
|
||||
},
|
||||
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true, return_data: ReturnData::empty() }),
|
||||
Ok(GasLeft::NeedsReturn { gas_left, data, apply_state }) => ext.ret(&gas_left, &data, apply_state).map(|gas_left| FinalizationResult {
|
||||
gas_left: gas_left,
|
||||
apply_state: apply_state,
|
||||
return_data: data,
|
||||
}),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ impl Factory {
|
||||
/// for caching jump destinations.
|
||||
pub fn new(evm: VMType, cache_size: usize) -> Self {
|
||||
Factory {
|
||||
evm,
|
||||
evm: evm,
|
||||
evm_cache: Arc::new(SharedCache::new(cache_size)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,10 +149,6 @@ enum_with_from_u8! {
|
||||
DIFFICULTY = 0x44,
|
||||
#[doc = "get the block's gas limit"]
|
||||
GASLIMIT = 0x45,
|
||||
#[doc = "get chain ID"]
|
||||
CHAINID = 0x46,
|
||||
#[doc = "get balance of own account"]
|
||||
SELFBALANCE = 0x47,
|
||||
|
||||
#[doc = "remove item from stack"]
|
||||
POP = 0x50,
|
||||
@@ -446,7 +442,12 @@ pub struct InstructionInfo {
|
||||
impl InstructionInfo {
|
||||
/// Create new instruction info.
|
||||
pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self {
|
||||
InstructionInfo { name, args, ret, tier }
|
||||
InstructionInfo {
|
||||
name: name,
|
||||
args: args,
|
||||
ret: ret,
|
||||
tier: tier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,8 +504,6 @@ lazy_static! {
|
||||
arr[NUMBER as usize] = Some(InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base));
|
||||
arr[DIFFICULTY as usize] = Some(InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base));
|
||||
arr[GASLIMIT as usize] = Some(InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base));
|
||||
arr[CHAINID as usize] = Some(InstructionInfo::new("CHAINID", 0, 1, GasPriceTier::Base));
|
||||
arr[SELFBALANCE as usize] = Some(InstructionInfo::new("SELFBALANCE", 0, 1, GasPriceTier::Low));
|
||||
arr[POP as usize] = Some(InstructionInfo::new("POP", 1, 0, GasPriceTier::Base));
|
||||
arr[MLOAD as usize] = Some(InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow));
|
||||
arr[MSTORE as usize] = Some(InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow));
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cmp;
|
||||
use ethereum_types::{BigEndianHash, U256};
|
||||
use ethereum_types::{U256, H256};
|
||||
use super::u256_to_address;
|
||||
|
||||
use {evm, vm};
|
||||
@@ -121,16 +121,12 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
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 = BigEndianHash::from_uint(stack.peek(0));
|
||||
let address = H256::from(stack.peek(0));
|
||||
let newval = stack.peek(1);
|
||||
let val = ext.storage_at(&address)?.into_uint();
|
||||
let val = U256::from(&*ext.storage_at(&address)?);
|
||||
|
||||
let gas = if schedule.eip1283 {
|
||||
let orig = ext.initial_storage_at(&address)?.into_uint();
|
||||
let orig = U256::from(&*ext.initial_storage_at(&address)?);
|
||||
calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval)
|
||||
} else {
|
||||
if val.is_zero() && !newval.is_zero() {
|
||||
|
||||
@@ -26,11 +26,10 @@ mod shared_cache;
|
||||
use std::marker::PhantomData;
|
||||
use std::{cmp, mem};
|
||||
use std::sync::Arc;
|
||||
use std::convert::TryFrom;
|
||||
use hash::keccak;
|
||||
use bytes::Bytes;
|
||||
use ethereum_types::{U256, U512, H256, Address, BigEndianHash};
|
||||
|
||||
use ethereum_types::{U256, H256, Address};
|
||||
use num_bigint::BigUint;
|
||||
|
||||
use vm::{
|
||||
self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult,
|
||||
@@ -63,6 +62,17 @@ const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 0
|
||||
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
|
||||
|
||||
fn to_biguint(x: U256) -> BigUint {
|
||||
let mut bytes = [0u8; 32];
|
||||
x.to_little_endian(&mut bytes);
|
||||
BigUint::from_bytes_le(&bytes)
|
||||
}
|
||||
|
||||
fn from_biguint(x: BigUint) -> U256 {
|
||||
let bytes = x.to_bytes_le();
|
||||
U256::from_little_endian(&bytes)
|
||||
}
|
||||
|
||||
/// Abstraction over raw vector of Bytes. Easier state management of PC.
|
||||
struct CodeReader {
|
||||
position: ProgramCounter,
|
||||
@@ -109,6 +119,8 @@ enum InstructionResult<Gas> {
|
||||
Trap(TrapKind),
|
||||
}
|
||||
|
||||
enum Never {}
|
||||
|
||||
/// ActionParams without code, so that it can be feed into CodeReader.
|
||||
#[derive(Debug)]
|
||||
struct InterpreterParams {
|
||||
@@ -166,6 +178,12 @@ pub enum InterpreterResult {
|
||||
Trap(TrapKind),
|
||||
}
|
||||
|
||||
impl From<vm::Error> for InterpreterResult {
|
||||
fn from(error: vm::Error) -> InterpreterResult {
|
||||
InterpreterResult::Done(Err(error))
|
||||
}
|
||||
}
|
||||
|
||||
/// Intepreter EVM implementation
|
||||
pub struct Interpreter<Cost: CostType> {
|
||||
mem: Vec<u8>,
|
||||
@@ -276,8 +294,6 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
cache, params, reader, informant,
|
||||
valid_jump_destinations, gasometer, stack,
|
||||
done: false,
|
||||
// Overridden in `step_inner` based on
|
||||
// the result of `ext.trace_next_instruction`.
|
||||
do_trace: true,
|
||||
mem: Vec::new(),
|
||||
return_data: ReturnData::empty(),
|
||||
@@ -298,26 +314,21 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let result = if self.gasometer.is_none() {
|
||||
InterpreterResult::Done(Err(vm::Error::OutOfGas))
|
||||
} else if self.reader.len() == 0 {
|
||||
let current_gas = self.gasometer
|
||||
.as_ref()
|
||||
.expect("Gasometer None case is checked above; qed")
|
||||
.current_gas
|
||||
.as_u256();
|
||||
InterpreterResult::Done(Ok(GasLeft::Known(current_gas)))
|
||||
InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_ref().expect("Gasometer None case is checked above; qed").current_gas.as_u256())))
|
||||
} else {
|
||||
self.step_inner(ext)
|
||||
self.step_inner(ext).err().expect("step_inner never returns Ok(()); qed")
|
||||
};
|
||||
|
||||
if let &InterpreterResult::Done(_) = &result {
|
||||
self.done = true;
|
||||
self.informant.done();
|
||||
}
|
||||
result
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Inner helper function for step.
|
||||
#[inline(always)]
|
||||
fn step_inner(&mut self, ext: &mut dyn vm::Ext) -> InterpreterResult {
|
||||
fn step_inner(&mut self, ext: &mut vm::Ext) -> Result<Never, InterpreterResult> {
|
||||
let result = match self.resume_result.take() {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
@@ -332,31 +343,22 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
|
||||
let instruction = match instruction {
|
||||
Some(i) => i,
|
||||
None => return InterpreterResult::Done(Err(vm::Error::BadInstruction {
|
||||
None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction {
|
||||
instruction: opcode
|
||||
})),
|
||||
}))),
|
||||
};
|
||||
|
||||
let info = instruction.info();
|
||||
self.last_stack_ret_len = info.ret;
|
||||
if let Err(e) = self.verify_instruction(ext, instruction, info) {
|
||||
return InterpreterResult::Done(Err(e));
|
||||
};
|
||||
self.verify_instruction(ext, instruction, info)?;
|
||||
|
||||
// Calculate gas cost
|
||||
let requirements = match self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size()) {
|
||||
Ok(t) => t,
|
||||
Err(e) => return InterpreterResult::Done(Err(e)),
|
||||
};
|
||||
let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?;
|
||||
if self.do_trace {
|
||||
ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack));
|
||||
}
|
||||
if let Err(e) = self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost) {
|
||||
if self.do_trace {
|
||||
ext.trace_failed();
|
||||
}
|
||||
return InterpreterResult::Done(Err(e));
|
||||
}
|
||||
|
||||
self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost)?;
|
||||
self.mem.expand(requirements.memory_required_size);
|
||||
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas;
|
||||
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost;
|
||||
@@ -365,24 +367,18 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
|
||||
// Execute instruction
|
||||
let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas;
|
||||
let result = match self.exec_instruction(
|
||||
let result = self.exec_instruction(
|
||||
current_gas, ext, instruction, requirements.provide_gas
|
||||
) {
|
||||
Err(x) => {
|
||||
if self.do_trace {
|
||||
ext.trace_failed();
|
||||
}
|
||||
return InterpreterResult::Done(Err(x));
|
||||
},
|
||||
Ok(x) => x,
|
||||
};
|
||||
)?;
|
||||
|
||||
evm_debug!({ self.informant.after_instruction(instruction) });
|
||||
|
||||
result
|
||||
},
|
||||
};
|
||||
|
||||
if let InstructionResult::Trap(trap) = result {
|
||||
return InterpreterResult::Trap(trap);
|
||||
return Err(InterpreterResult::Trap(trap));
|
||||
}
|
||||
|
||||
if let InstructionResult::UnusedGas(ref gas) = result {
|
||||
@@ -404,31 +400,28 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
self.valid_jump_destinations = Some(self.cache.jump_destinations(&self.params.code_hash, &self.reader.code));
|
||||
}
|
||||
let jump_destinations = self.valid_jump_destinations.as_ref().expect("jump_destinations are initialized on first jump; qed");
|
||||
let pos = match self.verify_jump(position, jump_destinations) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return InterpreterResult::Done(Err(e))
|
||||
};
|
||||
let pos = self.verify_jump(position, jump_destinations)?;
|
||||
self.reader.position = pos;
|
||||
},
|
||||
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
|
||||
let mem = mem::replace(&mut self.mem, Vec::new());
|
||||
return InterpreterResult::Done(Ok(GasLeft::NeedsReturn {
|
||||
return Err(InterpreterResult::Done(Ok(GasLeft::NeedsReturn {
|
||||
gas_left: gas.as_u256(),
|
||||
data: mem.into_return_data(init_off, init_size),
|
||||
apply_state: apply
|
||||
}));
|
||||
})));
|
||||
},
|
||||
InstructionResult::StopExecution => {
|
||||
return InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256())));
|
||||
return Err(InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256()))));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
if self.reader.position >= self.reader.len() {
|
||||
return InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256())));
|
||||
return Err(InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256()))));
|
||||
}
|
||||
|
||||
InterpreterResult::Continue
|
||||
Err(InterpreterResult::Continue)
|
||||
}
|
||||
|
||||
fn verify_instruction(&self, ext: &vm::Ext, instruction: Instruction, info: &InstructionInfo) -> vm::Result<()> {
|
||||
@@ -440,9 +433,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
|
||||
(instruction == instructions::REVERT && !schedule.have_revert) ||
|
||||
((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) ||
|
||||
(instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash) ||
|
||||
(instruction == instructions::CHAINID && !schedule.have_chain_id) ||
|
||||
(instruction == instructions::SELFBALANCE && !schedule.have_selfbalance)
|
||||
(instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash)
|
||||
{
|
||||
return Err(vm::Error::BadInstruction {
|
||||
instruction: instruction as u8
|
||||
@@ -529,7 +520,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let init_size = self.stack.pop_back();
|
||||
let address_scheme = match instruction {
|
||||
instructions::CREATE => CreateContractAddress::FromSenderAndNonce,
|
||||
instructions::CREATE2 => CreateContractAddress::FromSenderSaltAndCodeHash(BigEndianHash::from_uint(&self.stack.pop_back())),
|
||||
instructions::CREATE2 => CreateContractAddress::FromSenderSaltAndCodeHash(self.stack.pop_back().into()),
|
||||
_ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"),
|
||||
};
|
||||
|
||||
@@ -686,7 +677,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let size = self.stack.pop_back();
|
||||
let topics = self.stack.pop_n(no_of_topics)
|
||||
.iter()
|
||||
.map(BigEndianHash::from_uint)
|
||||
.map(H256::from)
|
||||
.collect();
|
||||
ext.log(topics, self.mem.read_slice(offset, size))?;
|
||||
},
|
||||
@@ -723,21 +714,21 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let offset = self.stack.pop_back();
|
||||
let size = self.stack.pop_back();
|
||||
let k = keccak(self.mem.read_slice(offset, size));
|
||||
self.stack.push(k.into_uint());
|
||||
self.stack.push(U256::from(&*k));
|
||||
},
|
||||
instructions::SLOAD => {
|
||||
let key = BigEndianHash::from_uint(&self.stack.pop_back());
|
||||
let word = ext.storage_at(&key)?.into_uint();
|
||||
let key = H256::from(&self.stack.pop_back());
|
||||
let word = U256::from(&*ext.storage_at(&key)?);
|
||||
self.stack.push(word);
|
||||
},
|
||||
instructions::SSTORE => {
|
||||
let address = BigEndianHash::from_uint(&self.stack.pop_back());
|
||||
let address = H256::from(&self.stack.pop_back());
|
||||
let val = self.stack.pop_back();
|
||||
|
||||
let current_val = ext.storage_at(&address)?.into_uint();
|
||||
let current_val = U256::from(&*ext.storage_at(&address)?);
|
||||
// Increase refund for clear
|
||||
if ext.schedule().eip1283 {
|
||||
let original_val = ext.initial_storage_at(&address)?.into_uint();
|
||||
let original_val = U256::from(&*ext.initial_storage_at(&address)?);
|
||||
gasometer::handle_eip1283_sstore_clears_refund(ext, &original_val, ¤t_val, &val);
|
||||
} else {
|
||||
if !current_val.is_zero() && val.is_zero() {
|
||||
@@ -745,7 +736,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
ext.add_sstore_refund(sstore_clears_schedule);
|
||||
}
|
||||
}
|
||||
ext.set_storage(address, BigEndianHash::from_uint(&val))?;
|
||||
ext.set_storage(address, H256::from(&val))?;
|
||||
},
|
||||
instructions::PC => {
|
||||
self.stack.push(U256::from(self.reader.position - 1));
|
||||
@@ -806,7 +797,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
instructions::EXTCODEHASH => {
|
||||
let address = u256_to_address(&self.stack.pop_back());
|
||||
let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero);
|
||||
self.stack.push(hash.into_uint());
|
||||
self.stack.push(U256::from(hash));
|
||||
},
|
||||
instructions::CALLDATACOPY => {
|
||||
Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &self.params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
|
||||
@@ -840,7 +831,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
instructions::BLOCKHASH => {
|
||||
let block_number = self.stack.pop_back();
|
||||
let block_hash = ext.blockhash(&block_number);
|
||||
self.stack.push(block_hash.into_uint());
|
||||
self.stack.push(U256::from(&*block_hash));
|
||||
},
|
||||
instructions::COINBASE => {
|
||||
self.stack.push(address_to_u256(ext.env_info().author.clone()));
|
||||
@@ -857,12 +848,6 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
instructions::GASLIMIT => {
|
||||
self.stack.push(ext.env_info().gas_limit.clone());
|
||||
},
|
||||
instructions::CHAINID => {
|
||||
self.stack.push(ext.chain_id().into())
|
||||
},
|
||||
instructions::SELFBALANCE => {
|
||||
self.stack.push(ext.balance(&self.params.address)?);
|
||||
}
|
||||
|
||||
// Stack instructions
|
||||
|
||||
@@ -1036,12 +1021,12 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let c = self.stack.pop_back();
|
||||
|
||||
self.stack.push(if !c.is_zero() {
|
||||
let a_512 = U512::from(a);
|
||||
let b_512 = U512::from(b);
|
||||
let c_512 = U512::from(c);
|
||||
let res = a_512 + b_512;
|
||||
let x = res % c_512;
|
||||
U256::try_from(x).expect("U512 % U256 fits U256; qed")
|
||||
let a_num = to_biguint(a);
|
||||
let b_num = to_biguint(b);
|
||||
let c_num = to_biguint(c);
|
||||
let res = a_num + b_num;
|
||||
let x = res % c_num;
|
||||
from_biguint(x)
|
||||
} else {
|
||||
U256::zero()
|
||||
});
|
||||
@@ -1052,12 +1037,12 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let c = self.stack.pop_back();
|
||||
|
||||
self.stack.push(if !c.is_zero() {
|
||||
let a_512 = U512::from(a);
|
||||
let b_512 = U512::from(b);
|
||||
let c_512 = U512::from(c);
|
||||
let res = a_512 * b_512;
|
||||
let x = res % c_512;
|
||||
U256::try_from(x).expect("U512 % U256 fits U256; qed")
|
||||
let a_num = to_biguint(a);
|
||||
let b_num = to_biguint(b);
|
||||
let c_num = to_biguint(c);
|
||||
let res = a_num * b_num;
|
||||
let x = res % c_num;
|
||||
from_biguint(x)
|
||||
} else {
|
||||
U256::zero()
|
||||
});
|
||||
@@ -1197,13 +1182,12 @@ fn set_sign(value: U256, sign: bool) -> U256 {
|
||||
|
||||
#[inline]
|
||||
fn u256_to_address(value: &U256) -> Address {
|
||||
let addr: H256 = BigEndianHash::from_uint(value);
|
||||
Address::from(addr)
|
||||
Address::from(H256::from(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn address_to_u256(value: Address) -> U256 {
|
||||
H256::from(value).into_uint()
|
||||
U256::from(&*H256::from(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1214,7 +1198,6 @@ mod tests {
|
||||
use factory::Factory;
|
||||
use vm::{self, Exec, ActionParams, ActionValue};
|
||||
use vm::tests::{FakeExt, test_finalize};
|
||||
use ethereum_types::Address;
|
||||
|
||||
fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box<Exec> {
|
||||
Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth())
|
||||
@@ -1225,13 +1208,13 @@ mod tests {
|
||||
let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = Address::from_low_u64_be(5);
|
||||
params.address = 5.into();
|
||||
params.gas = 300_000.into();
|
||||
params.gas_price = 1.into();
|
||||
params.value = ActionValue::Transfer(100_000.into());
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new();
|
||||
ext.balances.insert(Address::from_low_u64_be(5), 1_000_000_000.into());
|
||||
ext.balances.insert(5.into(), 1_000_000_000.into());
|
||||
ext.tracing = true;
|
||||
|
||||
let gas_left = {
|
||||
@@ -1248,12 +1231,12 @@ mod tests {
|
||||
let code = "6001600160000360003e00".from_hex().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = Address::from_low_u64_be(5);
|
||||
params.address = 5.into();
|
||||
params.gas = 300_000.into();
|
||||
params.gas_price = 1.into();
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new_byzantium();
|
||||
ext.balances.insert(Address::from_low_u64_be(5), 1_000_000_000.into());
|
||||
ext.balances.insert(5.into(), 1_000_000_000.into());
|
||||
ext.tracing = true;
|
||||
|
||||
let err = {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use hash::KECCAK_EMPTY;
|
||||
use parity_util_mem::{MallocSizeOf, MallocSizeOfOps};
|
||||
use heapsize::HeapSizeOf;
|
||||
use ethereum_types::H256;
|
||||
use parking_lot::Mutex;
|
||||
use memory_cache::MemoryLruCache;
|
||||
@@ -25,12 +25,11 @@ use super::super::instructions::{self, Instruction};
|
||||
|
||||
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Stub for a sharing `BitSet` data in cache (reference counted)
|
||||
/// and implementing MallocSizeOf on it.
|
||||
// stub for a HeapSizeOf implementation.
|
||||
struct Bits(Arc<BitSet>);
|
||||
|
||||
impl MallocSizeOf for Bits {
|
||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
impl HeapSizeOf for Bits {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
// dealing in bits here
|
||||
self.0.capacity() * 8
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
extern crate bit_set;
|
||||
extern crate ethereum_types;
|
||||
extern crate parking_lot;
|
||||
extern crate parity_util_mem;
|
||||
extern crate heapsize;
|
||||
extern crate vm;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate memory_cache;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate num_bigint;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
@@ -33,8 +34,6 @@ extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rustc_hex;
|
||||
#[cfg(test)]
|
||||
extern crate hex_literal;
|
||||
|
||||
pub mod evm;
|
||||
pub mod interpreter;
|
||||
|
||||
@@ -25,7 +25,6 @@ use vm::{self, ActionParams, ActionValue, Ext};
|
||||
use vm::tests::{FakeExt, FakeCall, FakeCallType, test_finalize};
|
||||
use factory::Factory;
|
||||
use vmtype::VMType;
|
||||
use hex_literal::hex;
|
||||
|
||||
evm_test!{test_add: test_add_int}
|
||||
fn test_add(factory: super::Factory) {
|
||||
@@ -109,32 +108,6 @@ fn test_origin(factory: super::Factory) {
|
||||
assert_store(&ext, 0, "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681");
|
||||
}
|
||||
|
||||
evm_test!{test_selfbalance: test_selfbalance_int}
|
||||
fn test_selfbalance(factory: super::Factory) {
|
||||
let own_addr = Address::from_str("1337000000000000000000000000000000000000").unwrap();
|
||||
// 47 SELFBALANCE
|
||||
// 60 ff PUSH ff
|
||||
// 55 SSTORE
|
||||
let code = hex!("47 60 ff 55").to_vec();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = own_addr.clone();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new_istanbul();
|
||||
ext.balances = {
|
||||
let mut x = HashMap::new();
|
||||
x.insert(own_addr, U256::from(1_025)); // 0x401
|
||||
x
|
||||
};
|
||||
let gas_left = {
|
||||
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
|
||||
};
|
||||
assert_eq!(gas_left, U256::from(79_992)); // TODO[dvdplm]: do the sums here, SELFBALANCE-5 + PUSH1-3 + ONEBYTE-4 + SSTORE-?? = 100_000 - 79_992
|
||||
assert_store(&ext, 0xff, "0000000000000000000000000000000000000000000000000000000000000401");
|
||||
}
|
||||
|
||||
evm_test!{test_sender: test_sender_int}
|
||||
fn test_sender(factory: super::Factory) {
|
||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
@@ -157,27 +130,6 @@ fn test_sender(factory: super::Factory) {
|
||||
assert_store(&ext, 0, "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681");
|
||||
}
|
||||
|
||||
evm_test!{test_chain_id: test_chain_id_int}
|
||||
fn test_chain_id(factory: super::Factory) {
|
||||
// 46 CHAINID
|
||||
// 60 00 PUSH 0
|
||||
// 55 SSTORE
|
||||
let code = hex!("46 60 00 55").to_vec();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new_istanbul().with_chain_id(9);
|
||||
|
||||
let gas_left = {
|
||||
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(79_995));
|
||||
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000009");
|
||||
}
|
||||
|
||||
evm_test!{test_extcodecopy: test_extcodecopy_int}
|
||||
fn test_extcodecopy(factory: super::Factory) {
|
||||
// 33 - sender
|
||||
@@ -287,7 +239,7 @@ fn test_blockhash(factory: super::Factory) {
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(79_974));
|
||||
assert_eq!(ext.store.get(&H256::zero()).unwrap(), &blockhash);
|
||||
assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash);
|
||||
}
|
||||
|
||||
evm_test!{test_calldataload: test_calldataload_int}
|
||||
@@ -310,6 +262,7 @@ fn test_calldataload(factory: super::Factory) {
|
||||
|
||||
assert_eq!(gas_left, U256::from(79_991));
|
||||
assert_store(&ext, 0, "23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23");
|
||||
|
||||
}
|
||||
|
||||
evm_test!{test_author: test_author_int}
|
||||
@@ -773,8 +726,8 @@ evm_test!{test_calls: test_calls_int}
|
||||
fn test_calls(factory: super::Factory) {
|
||||
let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap();
|
||||
|
||||
let address = Address::from_low_u64_be(0x155);
|
||||
let code_address = Address::from_low_u64_be(0x998);
|
||||
let address = Address::from(0x155);
|
||||
let code_address = Address::from(0x998);
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(150_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
@@ -819,7 +772,7 @@ evm_test!{test_create_in_staticcall: test_create_in_staticcall_int}
|
||||
fn test_create_in_staticcall(factory: super::Factory) {
|
||||
let code = "600060006064f000".from_hex().unwrap();
|
||||
|
||||
let address = Address::from_low_u64_be(0x155);
|
||||
let address = Address::from(0x155);
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
@@ -1113,5 +1066,5 @@ fn assert_set_contains<T : Debug + Eq + PartialEq + Hash>(set: &HashSet<T>, val:
|
||||
}
|
||||
|
||||
fn assert_store(ext: &FakeExt, pos: u64, val: &str) {
|
||||
assert_eq!(ext.store.get(&H256::from_low_u64_be(pos)).unwrap(), &H256::from_str(val).unwrap());
|
||||
assert_eq!(ext.store.get(&H256::from(pos)).unwrap(), &H256::from_str(val).unwrap());
|
||||
}
|
||||
|
||||
@@ -10,34 +10,32 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
log = "0.4"
|
||||
parity-bytes = "0.1"
|
||||
common-types = { path = "../types" }
|
||||
derive_more = "0.14.0"
|
||||
ethcore = { path = ".."}
|
||||
ethcore-db = { path = "../db" }
|
||||
ethcore-blockchain = { path = "../blockchain" }
|
||||
ethereum-types = "0.6.0"
|
||||
memory-db = "0.12.4"
|
||||
trie-db = "0.12.4"
|
||||
ethereum-types = "0.4"
|
||||
memory-db = "0.11.0"
|
||||
trie-db = "0.11.0"
|
||||
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
|
||||
ethcore-network = { path = "../../util/network" }
|
||||
ethcore-miner = { path = "../../miner" }
|
||||
ethcore-io = { path = "../../util/io" }
|
||||
hash-db = "0.12.4"
|
||||
parity-util-mem = "0.1"
|
||||
hash-db = "0.11.0"
|
||||
heapsize = "0.4"
|
||||
vm = { path = "../vm" }
|
||||
fastmap = { path = "../../util/fastmap" }
|
||||
failsafe = { version = "0.3.0", default-features = false, features = ["parking_lot_mutex"] }
|
||||
rlp = "0.4.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
smallvec = "0.6"
|
||||
futures = "0.1"
|
||||
rand = "0.6"
|
||||
rand = "0.4"
|
||||
itertools = "0.5"
|
||||
bincode = "1.1"
|
||||
bincode = "0.8.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parking_lot = "0.9"
|
||||
parking_lot = "0.7"
|
||||
stats = { path = "../../util/stats" }
|
||||
keccak-hash = "0.2.0"
|
||||
keccak-hash = "0.1"
|
||||
keccak-hasher = { path = "../../util/keccak-hasher" }
|
||||
triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" }
|
||||
kvdb = "0.1"
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
//! vector of all gas prices from a recent range of blocks.
|
||||
|
||||
use std::time::{Instant, Duration};
|
||||
use parity_util_mem::{MallocSizeOf, MallocSizeOfOps, MallocSizeOfExt};
|
||||
|
||||
use common_types::encoded;
|
||||
use common_types::BlockNumber;
|
||||
use common_types::receipt::Receipt;
|
||||
use ethereum_types::{H256, U256};
|
||||
use heapsize::HeapSizeOf;
|
||||
use memory_cache::MemoryLruCache;
|
||||
use stats::Corpus;
|
||||
|
||||
@@ -157,20 +157,18 @@ impl Cache {
|
||||
|
||||
/// Get the memory used.
|
||||
pub fn mem_used(&self) -> usize {
|
||||
self.malloc_size_of()
|
||||
self.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is fast method: it is possible to have a more exhaustive implementation
|
||||
impl MallocSizeOf for Cache {
|
||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
impl HeapSizeOf for Cache {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.headers.current_size()
|
||||
+ self.canon_hashes.current_size()
|
||||
+ self.bodies.current_size()
|
||||
+ self.receipts.current_size()
|
||||
+ self.chain_score.current_size()
|
||||
// `self.corpus` is skipped
|
||||
// TODO: + corpus
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,7 @@ pub struct BlockInfo {
|
||||
/// Build an in-memory CHT from a closure which provides necessary information
|
||||
/// about blocks. If the fetcher ever fails to provide the info, the CHT
|
||||
/// will not be generated.
|
||||
pub fn build<F>(cht_num: u64, mut fetcher: F)
|
||||
-> Option<CHT<MemoryDB<KeccakHasher, memory_db::HashKey<KeccakHasher>, DBValue>>>
|
||||
pub fn build<F>(cht_num: u64, mut fetcher: F) -> Option<CHT<MemoryDB<KeccakHasher, DBValue>>>
|
||||
where F: FnMut(BlockId) -> Option<BlockInfo>
|
||||
{
|
||||
let mut db = new_memory_db();
|
||||
@@ -105,7 +104,7 @@ pub fn build<F>(cht_num: u64, mut fetcher: F)
|
||||
let last_num = start_number(cht_num + 1) - 1;
|
||||
let mut id = BlockId::Number(last_num);
|
||||
|
||||
let mut root = H256::zero();
|
||||
let mut root = H256::default();
|
||||
|
||||
{
|
||||
let mut t = TrieDBMut::new(&mut db, &mut root);
|
||||
@@ -155,7 +154,7 @@ pub fn compute_root<I>(cht_num: u64, iterable: I) -> Option<H256>
|
||||
pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> {
|
||||
let mut db = new_memory_db();
|
||||
|
||||
for node in proof { db.insert(hash_db::EMPTY_PREFIX, &node[..]); }
|
||||
for node in proof { db.insert(&node[..]); }
|
||||
let res = match TrieDB::new(&db, &root) {
|
||||
Err(_) => return None,
|
||||
Ok(trie) => trie.get_with(&key!(num), |val: &[u8]| {
|
||||
|
||||
@@ -21,7 +21,8 @@ use std::sync::Arc;
|
||||
use common_types::encoded;
|
||||
use common_types::header::Header;
|
||||
use common_types::receipt::Receipt;
|
||||
use ethcore::engines::{Engine, StateDependentProof};
|
||||
use ethcore::engines::{EthEngine, StateDependentProof};
|
||||
use ethcore::machine::EthereumMachine;
|
||||
use ethereum_types::H256;
|
||||
use futures::future::IntoFuture;
|
||||
|
||||
@@ -47,8 +48,8 @@ pub trait ChainDataFetcher: Send + Sync + 'static {
|
||||
fn epoch_transition(
|
||||
&self,
|
||||
_hash: H256,
|
||||
_engine: Arc<Engine>,
|
||||
_checker: Arc<StateDependentProof>
|
||||
_engine: Arc<EthEngine>,
|
||||
_checker: Arc<StateDependentProof<EthereumMachine>>
|
||||
) -> Self::Transition;
|
||||
}
|
||||
|
||||
@@ -76,8 +77,8 @@ impl ChainDataFetcher for Unavailable {
|
||||
fn epoch_transition(
|
||||
&self,
|
||||
_hash: H256,
|
||||
_engine: Arc<Engine>,
|
||||
_checker: Arc<StateDependentProof>
|
||||
_engine: Arc<EthEngine>,
|
||||
_checker: Arc<StateDependentProof<EthereumMachine>>
|
||||
) -> Self::Transition {
|
||||
Err("fetching epoch transition proofs unavailable")
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@ use common_types::encoded;
|
||||
use common_types::header::Header;
|
||||
use common_types::ids::BlockId;
|
||||
use ethcore::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
|
||||
use ethcore::error::{Error, EthcoreResult, BlockError};
|
||||
use ethcore::error::{Error, EthcoreResult, ErrorKind as EthcoreErrorKind, BlockError};
|
||||
use ethcore::spec::{Spec, SpecHardcodedSync};
|
||||
use ethereum_types::{H256, H264, U256};
|
||||
use parity_util_mem::{MallocSizeOf, MallocSizeOfOps};
|
||||
use heapsize::HeapSizeOf;
|
||||
use kvdb::{DBTransaction, KeyValueDB};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use fastmap::H256FastMap;
|
||||
@@ -95,8 +95,8 @@ struct Entry {
|
||||
canonical_hash: H256,
|
||||
}
|
||||
|
||||
impl MallocSizeOf for Entry {
|
||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
impl HeapSizeOf for Entry {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
if self.candidates.spilled() {
|
||||
self.candidates.capacity() * ::std::mem::size_of::<Candidate>()
|
||||
} else {
|
||||
@@ -154,11 +154,8 @@ fn pending_transition_key(block_hash: H256) -> H264 {
|
||||
|
||||
let mut key = H264::default();
|
||||
|
||||
{
|
||||
let bytes = key.as_bytes_mut();
|
||||
bytes[0] = LEADING;
|
||||
bytes[1..].copy_from_slice(block_hash.as_bytes());
|
||||
}
|
||||
key[0] = LEADING;
|
||||
key.0[1..].copy_from_slice(&block_hash.0[..]);
|
||||
|
||||
key
|
||||
}
|
||||
@@ -168,11 +165,8 @@ fn transition_key(block_hash: H256) -> H264 {
|
||||
|
||||
let mut key = H264::default();
|
||||
|
||||
{
|
||||
let bytes = key.as_bytes_mut();
|
||||
bytes[0] = LEADING;
|
||||
bytes[1..].copy_from_slice(block_hash.as_bytes());
|
||||
}
|
||||
key[0] = LEADING;
|
||||
key.0[1..].copy_from_slice(&block_hash.0[..]);
|
||||
|
||||
key
|
||||
}
|
||||
@@ -202,21 +196,14 @@ pub enum HardcodedSync {
|
||||
Deny,
|
||||
}
|
||||
|
||||
#[derive(MallocSizeOf)]
|
||||
/// Header chain. See module docs for more details.
|
||||
pub struct HeaderChain {
|
||||
#[ignore_malloc_size_of = "ignored for performance reason"]
|
||||
genesis_header: encoded::Header, // special-case the genesis.
|
||||
candidates: RwLock<BTreeMap<u64, Entry>>,
|
||||
#[ignore_malloc_size_of = "ignored for performance reason"]
|
||||
best_block: RwLock<BlockDescriptor>,
|
||||
#[ignore_malloc_size_of = "ignored for performance reason"]
|
||||
live_epoch_proofs: RwLock<H256FastMap<EpochTransition>>,
|
||||
#[ignore_malloc_size_of = "ignored for performance reason"]
|
||||
db: Arc<KeyValueDB>,
|
||||
#[ignore_malloc_size_of = "ignored for performance reason"]
|
||||
col: Option<u32>,
|
||||
#[ignore_malloc_size_of = "ignored for performance reason"]
|
||||
cache: Arc<Mutex<Cache>>,
|
||||
}
|
||||
|
||||
@@ -250,7 +237,7 @@ impl HeaderChain {
|
||||
for c in &entry.candidates {
|
||||
let key = transition_key(c.hash);
|
||||
|
||||
if let Some(proof) = db.get(col, key.as_bytes())? {
|
||||
if let Some(proof) = db.get(col, &*key)? {
|
||||
live_epoch_proofs.insert(c.hash, EpochTransition {
|
||||
block_hash: c.hash,
|
||||
block_number: cur_number,
|
||||
@@ -267,7 +254,7 @@ impl HeaderChain {
|
||||
let best_block = {
|
||||
let era = match candidates.get(&curr.best_num) {
|
||||
Some(era) => era,
|
||||
None => return Err("Database corrupt: highest block referenced but no data.".into()),
|
||||
None => bail!("Database corrupt: highest block referenced but no data."),
|
||||
};
|
||||
|
||||
let best = &era.candidates[0];
|
||||
@@ -416,7 +403,7 @@ impl HeaderChain {
|
||||
.and_then(|entry| entry.candidates.iter().find(|c| c.hash == parent_hash))
|
||||
.map(|c| c.total_difficulty)
|
||||
.ok_or_else(|| BlockError::UnknownParent(parent_hash))
|
||||
.map_err(Error::Block)?
|
||||
.map_err(EthcoreErrorKind::Block)?
|
||||
};
|
||||
|
||||
parent_td + *header.difficulty()
|
||||
@@ -444,7 +431,7 @@ impl HeaderChain {
|
||||
}
|
||||
|
||||
if let Some(transition) = transition {
|
||||
transaction.put(self.col, transition_key(hash).as_bytes(), &transition.proof);
|
||||
transaction.put(self.col, &*transition_key(hash), &transition.proof);
|
||||
self.live_epoch_proofs.write().insert(hash, transition);
|
||||
}
|
||||
|
||||
@@ -521,10 +508,10 @@ impl HeaderChain {
|
||||
for ancient in &era_entry.candidates {
|
||||
let maybe_transition = live_epoch_proofs.remove(&ancient.hash);
|
||||
if let Some(epoch_transition) = maybe_transition {
|
||||
transaction.delete(self.col, transition_key(ancient.hash).as_bytes());
|
||||
transaction.delete(self.col, &*transition_key(ancient.hash));
|
||||
|
||||
if ancient.hash == era_entry.canonical_hash {
|
||||
last_canonical_transition = match self.db.get(self.col, ancient.hash.as_bytes()) {
|
||||
last_canonical_transition = match self.db.get(self.col, &ancient.hash) {
|
||||
Err(e) => {
|
||||
warn!(target: "chain", "Error reading from DB: {}\n
|
||||
", e);
|
||||
@@ -539,7 +526,7 @@ impl HeaderChain {
|
||||
}
|
||||
}
|
||||
|
||||
transaction.delete(self.col, ancient.hash.as_bytes());
|
||||
transaction.delete(self.col, &ancient.hash);
|
||||
}
|
||||
|
||||
let canon = &era_entry.candidates[0];
|
||||
@@ -589,7 +576,7 @@ impl HeaderChain {
|
||||
} else {
|
||||
let msg = format!("header of block #{} not found in DB ; database in an \
|
||||
inconsistent state", h_num);
|
||||
return Err(msg.into());
|
||||
bail!(msg);
|
||||
};
|
||||
|
||||
let decoded = header.decode().expect("decoding db value failed");
|
||||
@@ -660,7 +647,7 @@ impl HeaderChain {
|
||||
match cache.block_header(&hash) {
|
||||
Some(header) => Some(header),
|
||||
None => {
|
||||
match self.db.get(self.col, hash.as_bytes()) {
|
||||
match self.db.get(self.col, &hash) {
|
||||
Ok(db_value) => {
|
||||
db_value.map(|x| x.into_vec()).map(encoded::Header::new)
|
||||
.and_then(|header| {
|
||||
@@ -785,7 +772,7 @@ impl HeaderChain {
|
||||
|
||||
/// Get block status.
|
||||
pub fn status(&self, hash: &H256) -> BlockStatus {
|
||||
if self.db.get(self.col, hash.as_bytes()).ok().map_or(false, |x| x.is_some()) {
|
||||
if self.db.get(self.col, hash).ok().map_or(false, |x| x.is_some()) {
|
||||
BlockStatus::InChain
|
||||
} else {
|
||||
BlockStatus::Unknown
|
||||
@@ -795,13 +782,13 @@ impl HeaderChain {
|
||||
/// Insert a pending transition.
|
||||
pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: &PendingEpochTransition) {
|
||||
let key = pending_transition_key(hash);
|
||||
batch.put(self.col, key.as_bytes(), &*::rlp::encode(t));
|
||||
batch.put(self.col, &*key, &*::rlp::encode(t));
|
||||
}
|
||||
|
||||
/// Get pending transition for a specific block hash.
|
||||
pub fn pending_transition(&self, hash: H256) -> Option<PendingEpochTransition> {
|
||||
let key = pending_transition_key(hash);
|
||||
match self.db.get(self.col, key.as_bytes()) {
|
||||
match self.db.get(self.col, &*key) {
|
||||
Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")),
|
||||
Err(e) => {
|
||||
warn!(target: "chain", "Error reading from database: {}", e);
|
||||
@@ -845,6 +832,12 @@ impl HeaderChain {
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for HeaderChain {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.candidates.read().heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over a block's ancestry.
|
||||
pub struct AncestryIter<'a> {
|
||||
next: Option<encoded::Header>,
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
|
||||
use std::sync::{Weak, Arc};
|
||||
|
||||
use ethcore::client::{ClientReport, EnvInfo, EngineClient, ClientIoMessage, ForceUpdateSealing};
|
||||
use ethcore::engines::{epoch, Engine, EpochChange, EpochTransition, Proof};
|
||||
use ethcore::client::{ClientReport, EnvInfo, ClientIoMessage};
|
||||
use ethcore::engines::{epoch, EthEngine, EpochChange, EpochTransition, Proof};
|
||||
use ethcore::machine::EthereumMachine;
|
||||
use ethcore::error::{Error, EthcoreResult};
|
||||
use ethcore::verification::queue::{self, HeaderQueue};
|
||||
use ethcore::spec::{Spec, SpecHardcodedSync};
|
||||
@@ -33,7 +34,6 @@ use common_types::blockchain_info::BlockChainInfo;
|
||||
use common_types::encoded;
|
||||
use common_types::header::Header;
|
||||
use common_types::ids::BlockId;
|
||||
use common_types::verification_queue_info::VerificationQueueInfo as BlockQueueInfo;
|
||||
|
||||
use kvdb::KeyValueDB;
|
||||
|
||||
@@ -91,9 +91,6 @@ pub trait LightChainClient: Send + Sync {
|
||||
/// Attempt to get a block hash by block id.
|
||||
fn block_hash(&self, id: BlockId) -> Option<H256>;
|
||||
|
||||
/// Get block queue information.
|
||||
fn queue_info(&self) -> BlockQueueInfo;
|
||||
|
||||
/// Attempt to get block header by block id.
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
|
||||
|
||||
@@ -114,7 +111,7 @@ pub trait LightChainClient: Send + Sync {
|
||||
fn env_info(&self, id: BlockId) -> Option<EnvInfo>;
|
||||
|
||||
/// Get a handle to the consensus engine.
|
||||
fn engine(&self) -> &Arc<Engine>;
|
||||
fn engine(&self) -> &Arc<EthEngine>;
|
||||
|
||||
/// Query whether a block is known.
|
||||
fn is_known(&self, hash: &H256) -> bool;
|
||||
@@ -128,6 +125,9 @@ pub trait LightChainClient: Send + Sync {
|
||||
/// Flush the queue.
|
||||
fn flush_queue(&self);
|
||||
|
||||
/// Get queue info.
|
||||
fn queue_info(&self) -> queue::QueueInfo;
|
||||
|
||||
/// Get the `i`th CHT root.
|
||||
fn cht_root(&self, i: usize) -> Option<H256>;
|
||||
|
||||
@@ -159,7 +159,7 @@ impl<T: LightChainClient> AsLightClient for T {
|
||||
/// Light client implementation.
|
||||
pub struct Client<T> {
|
||||
queue: HeaderQueue,
|
||||
engine: Arc<Engine>,
|
||||
engine: Arc<EthEngine>,
|
||||
chain: HeaderChain,
|
||||
report: RwLock<ClientReport>,
|
||||
import_lock: Mutex<()>,
|
||||
@@ -361,9 +361,9 @@ impl<T: ChainDataFetcher> Client<T> {
|
||||
|
||||
/// Get blockchain mem usage in bytes.
|
||||
pub fn chain_mem_used(&self) -> usize {
|
||||
use parity_util_mem::MallocSizeOfExt;
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
self.chain.malloc_size_of()
|
||||
self.chain.heap_size_of_children()
|
||||
}
|
||||
|
||||
/// Set a closure to call when the client wants to be restarted.
|
||||
@@ -375,7 +375,7 @@ impl<T: ChainDataFetcher> Client<T> {
|
||||
}
|
||||
|
||||
/// Get a handle to the verification engine.
|
||||
pub fn engine(&self) -> &Arc<Engine> {
|
||||
pub fn engine(&self) -> &Arc<EthEngine> {
|
||||
&self.engine
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ impl<T: ChainDataFetcher> Client<T> {
|
||||
true
|
||||
}
|
||||
|
||||
fn check_epoch_signal(&self, verified_header: &Header) -> Result<Option<Proof>, T::Error> {
|
||||
fn check_epoch_signal(&self, verified_header: &Header) -> Result<Option<Proof<EthereumMachine>>, T::Error> {
|
||||
use ethcore::machine::{AuxiliaryRequest, AuxiliaryData};
|
||||
|
||||
let mut block: Option<Vec<u8>> = None;
|
||||
@@ -513,7 +513,7 @@ impl<T: ChainDataFetcher> Client<T> {
|
||||
}
|
||||
|
||||
// attempts to fetch the epoch proof from the network until successful.
|
||||
fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> {
|
||||
fn write_pending_proof(&self, header: &Header, proof: Proof<EthereumMachine>) -> Result<(), T::Error> {
|
||||
let proof = match proof {
|
||||
Proof::Known(known) => known,
|
||||
Proof::WithState(state_dependent) => {
|
||||
@@ -534,7 +534,6 @@ impl<T: ChainDataFetcher> Client<T> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: ChainDataFetcher> LightChainClient for Client<T> {
|
||||
fn add_listener(&self, listener: Weak<LightChainNotify>) {
|
||||
Client::add_listener(self, listener)
|
||||
@@ -542,10 +541,6 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) }
|
||||
|
||||
fn queue_info(&self) -> queue::QueueInfo {
|
||||
self.queue.queue_info()
|
||||
}
|
||||
|
||||
fn queue_header(&self, header: Header) -> EthcoreResult<H256> {
|
||||
self.import_header(header)
|
||||
}
|
||||
@@ -578,7 +573,7 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
|
||||
Client::env_info(self, id)
|
||||
}
|
||||
|
||||
fn engine(&self) -> &Arc<Engine> {
|
||||
fn engine(&self) -> &Arc<EthEngine> {
|
||||
Client::engine(self)
|
||||
}
|
||||
|
||||
@@ -605,6 +600,10 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
|
||||
Client::flush_queue(self);
|
||||
}
|
||||
|
||||
fn queue_info(&self) -> queue::QueueInfo {
|
||||
self.queue.queue_info()
|
||||
}
|
||||
|
||||
fn cht_root(&self, i: usize) -> Option<H256> {
|
||||
Client::cht_root(self, i)
|
||||
}
|
||||
@@ -620,8 +619,8 @@ impl<T: ChainDataFetcher> ::ethcore::client::ChainInfo for Client<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ChainDataFetcher> EngineClient for Client<T> {
|
||||
fn update_sealing(&self, _force: ForceUpdateSealing) {}
|
||||
impl<T: ChainDataFetcher> ::ethcore::client::EngineClient for Client<T> {
|
||||
fn update_sealing(&self) { }
|
||||
fn submit_seal(&self, _block_hash: H256, _seal: Vec<Vec<u8>>) { }
|
||||
fn broadcast_consensus_message(&self, _message: Vec<u8>) { }
|
||||
|
||||
|
||||
@@ -75,8 +75,9 @@ impl<T: ChainDataFetcher> Service<T> {
|
||||
io_service.channel(),
|
||||
cache,
|
||||
)?);
|
||||
spec.engine.register_client(Arc::downgrade(&client) as _);
|
||||
|
||||
io_service.register_handler(Arc::new(ImportBlocks(client.clone()))).map_err(Error::Io)?;
|
||||
spec.engine.register_client(Arc::downgrade(&client) as _);
|
||||
|
||||
Ok(Service {
|
||||
client,
|
||||
|
||||
@@ -61,12 +61,9 @@ extern crate ethcore_io as io;
|
||||
extern crate ethcore_network as network;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate ethereum_types;
|
||||
extern crate ethcore_miner as miner;
|
||||
extern crate ethcore;
|
||||
extern crate hash_db;
|
||||
extern crate parity_util_mem;
|
||||
extern crate parity_util_mem as mem;
|
||||
extern crate parity_util_mem as malloc_size_of;
|
||||
extern crate heapsize;
|
||||
extern crate failsafe;
|
||||
extern crate futures;
|
||||
extern crate itertools;
|
||||
@@ -88,7 +85,8 @@ extern crate keccak_hash as hash;
|
||||
extern crate triehash_ethereum as triehash;
|
||||
extern crate kvdb;
|
||||
extern crate memory_cache;
|
||||
extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate kvdb_memorydb;
|
||||
|
||||
@@ -199,15 +199,15 @@ pub struct FileStore(pub PathBuf);
|
||||
impl SampleStore for FileStore {
|
||||
fn load(&self) -> HashMap<Kind, VecDeque<u64>> {
|
||||
File::open(&self.0)
|
||||
.map_err(|e| Box::new(bincode::ErrorKind::Io(e)))
|
||||
.and_then(|mut file| bincode::deserialize_from(&mut file))
|
||||
.map_err(|e| Box::new(bincode::ErrorKind::IoError(e)))
|
||||
.and_then(|mut file| bincode::deserialize_from(&mut file, bincode::Infinite))
|
||||
.unwrap_or_else(|_| HashMap::new())
|
||||
}
|
||||
|
||||
fn store(&self, samples: &HashMap<Kind, VecDeque<u64>>) {
|
||||
let res = File::create(&self.0)
|
||||
.map_err(|e| Box::new(bincode::ErrorKind::Io(e)))
|
||||
.and_then(|mut file| bincode::serialize_into(&mut file, samples));
|
||||
.map_err(|e| Box::new(bincode::ErrorKind::IoError(e)))
|
||||
.and_then(|mut file| bincode::serialize_into(&mut file, samples, bincode::Infinite));
|
||||
|
||||
if let Err(e) = res {
|
||||
warn!(target: "pip", "Error writing light request timing samples to file: {}", e);
|
||||
|
||||
@@ -382,7 +382,7 @@ mod tests {
|
||||
protocol_version: 1,
|
||||
network_id: 1,
|
||||
head_td: U256::default(),
|
||||
head_hash: H256::zero(),
|
||||
head_hash: H256::default(),
|
||||
head_num: 10,
|
||||
genesis_hash: H256::zero(),
|
||||
last_head: None,
|
||||
@@ -417,7 +417,7 @@ mod tests {
|
||||
protocol_version: 1,
|
||||
network_id: 1,
|
||||
head_td: U256::default(),
|
||||
head_hash: H256::zero(),
|
||||
head_hash: H256::default(),
|
||||
head_num: 10,
|
||||
genesis_hash: H256::zero(),
|
||||
last_head: None,
|
||||
@@ -452,7 +452,7 @@ mod tests {
|
||||
protocol_version: 1,
|
||||
network_id: 1,
|
||||
head_td: U256::default(),
|
||||
head_hash: H256::zero(),
|
||||
head_hash: H256::default(),
|
||||
head_num: 10,
|
||||
genesis_hash: H256::zero(),
|
||||
last_head: None,
|
||||
@@ -550,7 +550,7 @@ mod tests {
|
||||
protocol_version: 1,
|
||||
network_id: 1,
|
||||
head_td: U256::default(),
|
||||
head_hash: H256::zero(),
|
||||
head_hash: H256::default(),
|
||||
head_num: 10,
|
||||
genesis_hash: H256::zero(),
|
||||
last_head: None,
|
||||
|
||||
@@ -22,7 +22,7 @@ use common_types::encoded;
|
||||
use common_types::ids::BlockId;
|
||||
use common_types::transaction::{Action, PendingTransaction};
|
||||
use ethcore::client::{EachBlockWith, TestBlockChainClient};
|
||||
use ethereum_types::{H256, U256, Address, BigEndianHash};
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use net::context::IoContext;
|
||||
use net::load_timer::MOVING_SAMPLE_SIZE;
|
||||
use net::status::{Capabilities, Status};
|
||||
@@ -158,7 +158,7 @@ impl Provider for TestProvider {
|
||||
|
||||
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
|
||||
Some(CodeResponse {
|
||||
code: req.block_hash.as_bytes().iter().chain(req.code_hash.as_bytes().iter()).cloned().collect(),
|
||||
code: req.block_hash.iter().chain(req.code_hash.iter()).cloned().collect(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ fn genesis_mismatch() {
|
||||
let (provider, proto) = setup(capabilities);
|
||||
|
||||
let mut status = status(provider.client.chain_info());
|
||||
status.genesis_hash = H256::zero();
|
||||
status.genesis_hash = H256::default();
|
||||
|
||||
let packet_body = write_handshake(&status, &capabilities, &proto);
|
||||
|
||||
@@ -472,16 +472,16 @@ fn get_state_proofs() {
|
||||
}
|
||||
|
||||
let req_id = 112;
|
||||
let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344));
|
||||
let key2: H256 = BigEndianHash::from_uint(&U256::from(99988887));
|
||||
let key1: H256 = U256::from(11223344).into();
|
||||
let key2: H256 = U256::from(99988887).into();
|
||||
|
||||
let mut builder = Builder::default();
|
||||
builder.push(Request::Account(IncompleteAccountRequest {
|
||||
block_hash: H256::zero().into(),
|
||||
block_hash: H256::default().into(),
|
||||
address_hash: key1.into(),
|
||||
})).unwrap();
|
||||
builder.push(Request::Storage(IncompleteStorageRequest {
|
||||
block_hash: H256::zero().into(),
|
||||
block_hash: H256::default().into(),
|
||||
address_hash: key1.into(),
|
||||
key_hash: key2.into(),
|
||||
})).unwrap();
|
||||
@@ -492,11 +492,11 @@ fn get_state_proofs() {
|
||||
let response = {
|
||||
let responses = vec![
|
||||
Response::Account(provider.account_proof(CompleteAccountRequest {
|
||||
block_hash: H256::zero(),
|
||||
block_hash: H256::default(),
|
||||
address_hash: key1,
|
||||
}).unwrap()),
|
||||
Response::Storage(provider.storage_proof(CompleteStorageRequest {
|
||||
block_hash: H256::zero(),
|
||||
block_hash: H256::default(),
|
||||
address_hash: key1,
|
||||
key_hash: key2,
|
||||
}).unwrap()),
|
||||
@@ -529,8 +529,8 @@ fn get_contract_code() {
|
||||
}
|
||||
|
||||
let req_id = 112;
|
||||
let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344));
|
||||
let key2: H256 = BigEndianHash::from_uint(&U256::from(99988887));
|
||||
let key1: H256 = U256::from(11223344).into();
|
||||
let key2: H256 = U256::from(99988887).into();
|
||||
|
||||
let request = Request::Code(IncompleteCodeRequest {
|
||||
block_hash: key1.into(),
|
||||
@@ -541,7 +541,7 @@ fn get_contract_code() {
|
||||
let request_body = make_packet(req_id, &requests);
|
||||
let response = {
|
||||
let response = vec![Response::Code(CodeResponse {
|
||||
code: key1.as_bytes().iter().chain(key2.as_bytes().iter()).cloned().collect(),
|
||||
code: key1.iter().chain(key2.iter()).cloned().collect(),
|
||||
})];
|
||||
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
@@ -616,9 +616,9 @@ fn proof_of_execution() {
|
||||
|
||||
let req_id = 112;
|
||||
let mut request = Request::Execution(request::IncompleteExecutionRequest {
|
||||
block_hash: H256::zero().into(),
|
||||
from: Address::zero(),
|
||||
action: Action::Call(Address::zero()),
|
||||
block_hash: H256::default().into(),
|
||||
from: Address::default(),
|
||||
action: Action::Call(Address::default()),
|
||||
gas: 100.into(),
|
||||
gas_price: 0.into(),
|
||||
value: 0.into(),
|
||||
@@ -755,7 +755,7 @@ fn get_transaction_index() {
|
||||
}
|
||||
|
||||
let req_id = 112;
|
||||
let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344));
|
||||
let key1: H256 = U256::from(11223344).into();
|
||||
|
||||
let request = Request::TransactionIndex(IncompleteTransactionIndexRequest {
|
||||
hash: key1.into(),
|
||||
|
||||
@@ -66,31 +66,32 @@ pub const DEFAULT_NUM_CONSECUTIVE_FAILED_REQUESTS: usize = 1;
|
||||
|
||||
/// OnDemand related errors
|
||||
pub mod error {
|
||||
// Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting`
|
||||
// https://github.com/paritytech/parity-ethereum/issues/10302
|
||||
#![allow(deprecated)]
|
||||
|
||||
use futures::sync::oneshot::Canceled;
|
||||
|
||||
/// OnDemand Error
|
||||
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||
pub enum Error {
|
||||
/// Canceled oneshot channel
|
||||
ChannelCanceled(Canceled),
|
||||
/// Timeout bad response
|
||||
BadResponse(String),
|
||||
/// OnDemand requests limit exceeded
|
||||
#[display(fmt = "OnDemand request maximum backoff iterations exceeded")]
|
||||
RequestLimit,
|
||||
}
|
||||
error_chain! {
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::ChannelCanceled(err) => Some(err),
|
||||
_ => None,
|
||||
foreign_links {
|
||||
ChannelCanceled(Canceled) #[doc = "Canceled oneshot channel"];
|
||||
}
|
||||
|
||||
errors {
|
||||
#[doc = "Timeout bad response"]
|
||||
BadResponse(err: String) {
|
||||
description("Max response evaluation time exceeded")
|
||||
display("{}", err)
|
||||
}
|
||||
|
||||
#[doc = "OnDemand requests limit exceeded"]
|
||||
RequestLimit {
|
||||
description("OnDemand request maximum backoff iterations exceeded")
|
||||
display("OnDemand request maximum backoff iterations exceeded")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// OnDemand Result
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
}
|
||||
|
||||
/// Public interface for performing network requests `OnDemand`
|
||||
@@ -271,7 +272,7 @@ impl Pending {
|
||||
response_err
|
||||
);
|
||||
|
||||
let err = self::error::Error::BadResponse(err);
|
||||
let err = self::error::ErrorKind::BadResponse(err);
|
||||
if self.sender.send(Err(err.into())).is_err() {
|
||||
debug!(target: "on_demand", "Dropped oneshot channel receiver on no response");
|
||||
}
|
||||
@@ -279,7 +280,7 @@ impl Pending {
|
||||
|
||||
// returning a peer discovery timeout during query attempts
|
||||
fn request_limit_reached(self) {
|
||||
let err = self::error::Error::RequestLimit;
|
||||
let err = self::error::ErrorKind::RequestLimit;
|
||||
if self.sender.send(Err(err.into())).is_err() {
|
||||
debug!(target: "on_demand", "Dropped oneshot channel receiver on time out");
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ use common_types::basic_account::BasicAccount;
|
||||
use common_types::encoded;
|
||||
use common_types::receipt::Receipt;
|
||||
use common_types::transaction::SignedTransaction;
|
||||
use ethcore::engines::{Engine, StateDependentProof};
|
||||
use ethcore::engines::{EthEngine, StateDependentProof};
|
||||
use ethcore::machine::EthereumMachine;
|
||||
use ethcore::state::{self, ProvedExecution};
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use ethtrie::{TrieError, TrieDB};
|
||||
@@ -980,9 +981,9 @@ impl Account {
|
||||
let state_root = header.state_root();
|
||||
|
||||
let mut db = journaldb::new_memory_db();
|
||||
for node in proof { db.insert(hash_db::EMPTY_PREFIX, &node[..]); }
|
||||
for node in proof { db.insert(&node[..]); }
|
||||
|
||||
match TrieDB::new(&db, &state_root).and_then(|t| t.get(keccak(&self.address).as_bytes()))? {
|
||||
match TrieDB::new(&db, &state_root).and_then(|t| t.get(&keccak(&self.address)))? {
|
||||
Some(val) => {
|
||||
let rlp = Rlp::new(&val);
|
||||
Ok(Some(BasicAccount {
|
||||
@@ -1037,7 +1038,7 @@ pub struct TransactionProof {
|
||||
// TODO: it's not really possible to provide this if the header is unknown.
|
||||
pub env_info: EnvInfo,
|
||||
/// Consensus engine.
|
||||
pub engine: Arc<Engine>,
|
||||
pub engine: Arc<EthEngine>,
|
||||
}
|
||||
|
||||
impl TransactionProof {
|
||||
@@ -1080,9 +1081,9 @@ pub struct Signal {
|
||||
/// Block hash and number to fetch proof for.
|
||||
pub hash: H256,
|
||||
/// Consensus engine, used to check the proof.
|
||||
pub engine: Arc<Engine>,
|
||||
pub engine: Arc<EthEngine>,
|
||||
/// Special checker for the proof.
|
||||
pub proof_check: Arc<StateDependentProof>,
|
||||
pub proof_check: Arc<StateDependentProof<EthereumMachine>>,
|
||||
}
|
||||
|
||||
impl Signal {
|
||||
@@ -1161,7 +1162,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn check_header_with_ancestors() {
|
||||
let mut last_header_hash = H256::zero();
|
||||
let mut last_header_hash = H256::default();
|
||||
let mut headers = (0..11).map(|num| {
|
||||
let mut header = Header::new();
|
||||
header.set_number(num);
|
||||
@@ -1277,7 +1278,7 @@ mod tests {
|
||||
fn check_state_proof() {
|
||||
use rlp::RlpStream;
|
||||
|
||||
let mut root = H256::zero();
|
||||
let mut root = H256::default();
|
||||
let mut db = journaldb::new_memory_db();
|
||||
let mut header = Header::new();
|
||||
header.set_number(123_456);
|
||||
@@ -1297,17 +1298,17 @@ mod tests {
|
||||
let mut trie = SecTrieDBMut::new(&mut db, &mut root);
|
||||
for _ in 0..100 {
|
||||
let address = Address::random();
|
||||
trie.insert(address.as_bytes(), &rand_acc()).unwrap();
|
||||
trie.insert(&*address, &rand_acc()).unwrap();
|
||||
}
|
||||
|
||||
trie.insert(addr.as_bytes(), &rand_acc()).unwrap();
|
||||
trie.insert(&*addr, &rand_acc()).unwrap();
|
||||
}
|
||||
|
||||
let proof = {
|
||||
let trie = SecTrieDB::new(&db, &root).unwrap();
|
||||
let mut recorder = Recorder::new();
|
||||
|
||||
trie.get_with(addr.as_bytes(), &mut recorder).unwrap().unwrap();
|
||||
trie.get_with(&*addr, &mut recorder).unwrap().unwrap();
|
||||
|
||||
recorder.drain().into_iter().map(|r| r.data).collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
@@ -117,9 +117,9 @@ fn dummy_status() -> Status {
|
||||
protocol_version: 1,
|
||||
network_id: 999,
|
||||
head_td: 1.into(),
|
||||
head_hash: H256::zero(),
|
||||
head_hash: H256::default(),
|
||||
head_num: 1359,
|
||||
genesis_hash: H256::zero(),
|
||||
genesis_hash: H256::default(),
|
||||
last_head: None,
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ fn detects_hangup() {
|
||||
let on_demand = Harness::create().service;
|
||||
let result = on_demand.request_raw(
|
||||
&Context::NoOp,
|
||||
vec![request::HeaderByHash(H256::zero().into()).into()],
|
||||
vec![request::HeaderByHash(H256::default().into()).into()],
|
||||
);
|
||||
|
||||
assert_eq!(on_demand.pending.read().len(), 1);
|
||||
@@ -199,7 +199,7 @@ fn no_capabilities() {
|
||||
|
||||
let _recv = harness.service.request_raw(
|
||||
&Context::NoOp,
|
||||
vec![request::HeaderByHash(H256::zero().into()).into()]
|
||||
vec![request::HeaderByHash(H256::default().into()).into()]
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(harness.service.pending.read().len(), 1);
|
||||
@@ -395,7 +395,7 @@ fn wrong_kind() {
|
||||
|
||||
let _recv = harness.service.request_raw(
|
||||
&Context::NoOp,
|
||||
vec![request::HeaderByHash(H256::zero().into()).into()]
|
||||
vec![request::HeaderByHash(H256::default().into()).into()]
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(harness.service.pending.read().len(), 1);
|
||||
|
||||
@@ -24,15 +24,12 @@
|
||||
//! address-wise manner.
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use common_types::transaction::{self, Condition, PendingTransaction, SignedTransaction};
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use fastmap::H256FastMap;
|
||||
use futures::sync::mpsc;
|
||||
use miner::pool::TxStatus;
|
||||
|
||||
// Knowledge of an account's current nonce.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -129,13 +126,14 @@ pub enum ImportDestination {
|
||||
Future,
|
||||
}
|
||||
|
||||
type Listener = Box<Fn(&[H256]) + Send + Sync>;
|
||||
|
||||
/// Light transaction queue. See module docs for more details.
|
||||
#[derive(Default)]
|
||||
pub struct TransactionQueue {
|
||||
by_account: HashMap<Address, AccountTransactions>,
|
||||
by_hash: H256FastMap<PendingTransaction>,
|
||||
pending_listeners: Vec<mpsc::UnboundedSender<Arc<Vec<H256>>>>,
|
||||
full_listeners: Vec<mpsc::UnboundedSender<Arc<Vec<(H256, TxStatus)>>>>,
|
||||
listeners: Vec<Listener>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TransactionQueue {
|
||||
@@ -143,8 +141,7 @@ impl fmt::Debug for TransactionQueue {
|
||||
fmt.debug_struct("TransactionQueue")
|
||||
.field("by_account", &self.by_account)
|
||||
.field("by_hash", &self.by_hash)
|
||||
.field("pending_listeners", &self.pending_listeners.len())
|
||||
.field("full_listeners", &self.pending_listeners.len())
|
||||
.field("listeners", &self.listeners.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -234,7 +231,7 @@ impl TransactionQueue {
|
||||
};
|
||||
|
||||
self.by_hash.insert(hash, tx);
|
||||
self.notify(&promoted, TxStatus::Added);
|
||||
self.notify(&promoted);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -346,8 +343,6 @@ impl TransactionQueue {
|
||||
trace!(target: "txqueue", "Culled {} old transactions from sender {} (nonce={})",
|
||||
removed_hashes.len(), address, cur_nonce);
|
||||
|
||||
self.notify(&removed_hashes, TxStatus::Culled);
|
||||
|
||||
for hash in removed_hashes {
|
||||
self.by_hash.remove(&hash);
|
||||
}
|
||||
@@ -359,40 +354,15 @@ impl TransactionQueue {
|
||||
}
|
||||
|
||||
/// Add a transaction queue listener.
|
||||
pub fn pending_transactions_receiver(&mut self) -> mpsc::UnboundedReceiver<Arc<Vec<H256>>> {
|
||||
let (sender, receiver) = mpsc::unbounded();
|
||||
self.pending_listeners.push(sender);
|
||||
receiver
|
||||
}
|
||||
|
||||
/// Add a transaction queue listener.
|
||||
pub fn full_transactions_receiver(&mut self) -> mpsc::UnboundedReceiver<Arc<Vec<(H256, TxStatus)>>> {
|
||||
let (sender, receiver) = mpsc::unbounded();
|
||||
self.full_listeners.push(sender);
|
||||
receiver
|
||||
pub fn add_listener(&mut self, f: Listener) {
|
||||
self.listeners.push(f);
|
||||
}
|
||||
|
||||
/// Notifies all listeners about new pending transaction.
|
||||
fn notify(&mut self, hashes: &[H256], status: TxStatus) {
|
||||
if status == TxStatus::Added {
|
||||
let to_pending_send: Arc<Vec<H256>> = Arc::new(
|
||||
hashes
|
||||
.into_iter()
|
||||
.map(|hash| hash.clone())
|
||||
.collect()
|
||||
);
|
||||
self.pending_listeners.retain(|listener| listener.unbounded_send(to_pending_send.clone()).is_ok());
|
||||
|
||||
fn notify(&self, hashes: &[H256]) {
|
||||
for listener in &self.listeners {
|
||||
listener(hashes)
|
||||
}
|
||||
|
||||
let to_full_send: Arc<Vec<(H256, TxStatus)>> = Arc::new(
|
||||
hashes
|
||||
.into_iter()
|
||||
.map(|hash| (hash.clone(), status))
|
||||
.collect()
|
||||
);
|
||||
|
||||
self.full_listeners.retain(|listener| listener.unbounded_send(to_full_send.clone()).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +374,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn queued_senders() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = Transaction::default().fake_sign(sender);
|
||||
|
||||
@@ -420,7 +390,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn next_nonce() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
for i in (0..5).chain(10..15) {
|
||||
@@ -451,7 +421,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn current_to_future() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
for i in 5..10 {
|
||||
@@ -494,7 +464,7 @@ mod tests {
|
||||
#[test]
|
||||
fn conditional() {
|
||||
let mut txq = TransactionQueue::default();
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
|
||||
for i in 0..5 {
|
||||
let mut tx = Transaction::default();
|
||||
@@ -516,7 +486,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cull_from_future() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
for i in (0..1).chain(3..10) {
|
||||
@@ -536,7 +506,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn import_old() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let mut tx_a = Transaction::default();
|
||||
@@ -553,7 +523,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn replace_is_removed() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let tx_b: PendingTransaction = Transaction::default().fake_sign(sender).into();
|
||||
@@ -573,7 +543,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn future_transactions() {
|
||||
let sender = Address::zero();
|
||||
let sender = Address::default();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
for i in (0..1).chain(3..10) {
|
||||
|
||||
@@ -220,7 +220,7 @@ mod tests {
|
||||
num: 100.into(),
|
||||
})).unwrap();
|
||||
builder.push(Request::Receipts(IncompleteReceiptsRequest {
|
||||
hash: H256::zero().into(),
|
||||
hash: H256::default().into(),
|
||||
})).unwrap();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ mod tests {
|
||||
})).unwrap();
|
||||
|
||||
let mut batch = builder.build();
|
||||
batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(H256::from_low_u64_be(42))));
|
||||
batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(42.into())));
|
||||
|
||||
assert!(batch.next_complete().is_some());
|
||||
batch.answered += 1;
|
||||
@@ -289,7 +289,7 @@ mod tests {
|
||||
assert!(batch.next_complete().is_some());
|
||||
let hdr_proof_res = header_proof::Response {
|
||||
proof: vec![],
|
||||
hash: H256::from_low_u64_be(12),
|
||||
hash: 12.into(),
|
||||
td: 21.into(),
|
||||
};
|
||||
batch.supply_response_unchecked(&hdr_proof_res);
|
||||
@@ -308,7 +308,7 @@ mod tests {
|
||||
})).unwrap();
|
||||
|
||||
let mut batch = builder.build();
|
||||
batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(H256::from_low_u64_be(42))));
|
||||
batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(42.into())));
|
||||
|
||||
assert!(batch.next_complete().is_some());
|
||||
batch.answered += 1;
|
||||
|
||||
@@ -1648,7 +1648,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hash_or_number_roundtrip() {
|
||||
let hash = HashOrNumber::Hash(H256::zero());
|
||||
let hash = HashOrNumber::Hash(H256::default());
|
||||
let number = HashOrNumber::Number(5);
|
||||
|
||||
check_roundtrip(hash);
|
||||
@@ -1808,7 +1808,7 @@ mod tests {
|
||||
let full_req = Request::Storage(req.clone());
|
||||
let res = StorageResponse {
|
||||
proof: vec![vec![1, 2, 3], vec![4, 5, 6]],
|
||||
value: H256::zero(),
|
||||
value: H256::default(),
|
||||
};
|
||||
let full_res = Response::Storage(res.clone());
|
||||
|
||||
@@ -1909,7 +1909,7 @@ mod tests {
|
||||
code_hash: Default::default(),
|
||||
storage_root: Default::default()
|
||||
}),
|
||||
Response::Storage(StorageResponse { proof: vec![], value: H256::zero() }),
|
||||
Response::Storage(StorageResponse { proof: vec![], value: H256::default() }),
|
||||
Response::Code(CodeResponse { code: vec![1, 2, 3, 4, 5] }),
|
||||
Response::Execution(ExecutionResponse { items: vec![] }),
|
||||
];
|
||||
|
||||
@@ -10,12 +10,12 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
ethcore = { path = ".."}
|
||||
ethcore-network = { path = "../../util/network" }
|
||||
ethcore-network-devp2p = { path = "../../util/network-devp2p" }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
log = "0.4"
|
||||
parking_lot = "0.9"
|
||||
ethabi = "8.0"
|
||||
ethabi-derive = "8.0"
|
||||
ethabi-contract = "8.0"
|
||||
parking_lot = "0.7"
|
||||
ethabi = "6.0"
|
||||
ethabi-derive = "6.0"
|
||||
ethabi-contract = "6.0"
|
||||
lru-cache = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -37,17 +37,13 @@ extern crate tempdir;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::sync::Weak;
|
||||
|
||||
use ethcore::client::{BlockChainClient, BlockId, ChainNotify, NewBlocks};
|
||||
|
||||
use ethcore::client::{BlockChainClient, BlockId};
|
||||
use ethereum_types::{H256, Address};
|
||||
use ethabi::FunctionOutputDecoder;
|
||||
use network::{ConnectionFilter, ConnectionDirection};
|
||||
use devp2p::NodeId;
|
||||
use devp2p::MAX_NODES_IN_TABLE;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use_contract!(peer_set, "res/peer_set.json");
|
||||
|
||||
@@ -55,27 +51,14 @@ use_contract!(peer_set, "res/peer_set.json");
|
||||
pub struct NodeFilter {
|
||||
client: Weak<BlockChainClient>,
|
||||
contract_address: Address,
|
||||
cache: RwLock<Cache>
|
||||
}
|
||||
|
||||
struct Cache {
|
||||
cache: HashMap<NodeId, bool>,
|
||||
order: VecDeque<NodeId>
|
||||
}
|
||||
|
||||
// Increase cache size due to possible reserved peers, which do not count in the node table size
|
||||
pub const CACHE_SIZE: usize = MAX_NODES_IN_TABLE + 1024;
|
||||
|
||||
impl NodeFilter {
|
||||
/// Create a new instance. Accepts a contract address.
|
||||
pub fn new(client: Weak<BlockChainClient>, contract_address: Address) -> NodeFilter {
|
||||
NodeFilter {
|
||||
client,
|
||||
contract_address,
|
||||
cache: RwLock::new(Cache{
|
||||
cache: HashMap::with_capacity(CACHE_SIZE),
|
||||
order: VecDeque::with_capacity(CACHE_SIZE)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,10 +70,6 @@ impl ConnectionFilter for NodeFilter {
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if let Some(allowed) = self.cache.read().cache.get(connecting_id) {
|
||||
return *allowed;
|
||||
}
|
||||
|
||||
let address = self.contract_address;
|
||||
let own_low = H256::from_slice(&own_id[0..32]);
|
||||
let own_high = H256::from_slice(&own_id[32..64]);
|
||||
@@ -104,23 +83,8 @@ impl ConnectionFilter for NodeFilter {
|
||||
debug!("Error callling peer set contract: {:?}", e);
|
||||
false
|
||||
});
|
||||
let mut cache = self.cache.write();
|
||||
if cache.cache.len() == CACHE_SIZE {
|
||||
let popped = cache.order.pop_front().expect("the cache is full so there's at least one item we can pop; qed");
|
||||
cache.cache.remove(&popped);
|
||||
};
|
||||
if cache.cache.insert(*connecting_id, allowed).is_none() {
|
||||
cache.order.push_back(*connecting_id);
|
||||
}
|
||||
allowed
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainNotify for NodeFilter {
|
||||
fn new_blocks(&self, _new_blocks: NewBlocks) {
|
||||
let mut cache = self.cache.write();
|
||||
cache.cache.clear();
|
||||
cache.order.clear();
|
||||
allowed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,13 +99,11 @@ mod test {
|
||||
use io::IoChannel;
|
||||
use super::NodeFilter;
|
||||
use tempdir::TempDir;
|
||||
use ethereum_types::Address;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Contract code: https://gist.github.com/arkpar/467dbcc73cbb85b0997a7a10ffa0695f
|
||||
#[test]
|
||||
fn node_filter() {
|
||||
let contract_addr = Address::from_str("0000000000000000000000000000000000000005").unwrap();
|
||||
let contract_addr = "0000000000000000000000000000000000000005".into();
|
||||
let data = include_bytes!("../res/node_filter.json");
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let spec = Spec::load(&tempdir.path(), &data[..]).unwrap();
|
||||
@@ -155,11 +117,11 @@ mod test {
|
||||
IoChannel::disconnected(),
|
||||
).unwrap();
|
||||
let filter = NodeFilter::new(Arc::downgrade(&client) as Weak<BlockChainClient>, contract_addr);
|
||||
let self1 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002").unwrap();
|
||||
let self2 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003").unwrap();
|
||||
let node1 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012").unwrap();
|
||||
let node2 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022").unwrap();
|
||||
let nodex = NodeId::from_str("77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let self1: NodeId = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002".into();
|
||||
let self2: NodeId = "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003".into();
|
||||
let node1: NodeId = "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012".into();
|
||||
let node2: NodeId = "00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022".into();
|
||||
let nodex: NodeId = "77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||
|
||||
assert!(filter.connection_allowed(&self1, &node1, ConnectionDirection::Inbound));
|
||||
assert!(filter.connection_allowed(&self1, &nodex, ConnectionDirection::Inbound));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user