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!
|
||||
|
||||
✄ -----------------------------------------------------------------------------
|
||||
@@ -153,7 +153,7 @@ build-android:
|
||||
|
||||
build-linux:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
# only: *releaseable_branches
|
||||
|
||||
build-linux-i386:
|
||||
<<: *build-on-linux
|
||||
|
||||
240
CHANGELOG.md
240
CHANGELOG.md
@@ -1,185 +1,95 @@
|
||||
## Parity-Ethereum [v2.6.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.1)
|
||||
## Parity-Ethereum [v2.5.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.5)
|
||||
|
||||
Parity-Ethereum 2.6.1-beta is a patch release that improves stability.
|
||||
Parity-Ethereum v2.5.6-stable is a bugfix 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.
|
||||
* Allow specifying hostnames for node URLs
|
||||
* Fix a bug where archive nodes were losing peers
|
||||
|
||||
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)
|
||||
* 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 2.6.0-beta is a minor release that stabilizes the 2.6 branch by
|
||||
marking it as a beta release.
|
||||
## Parity-Ethereum [v2.5.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.5)
|
||||
|
||||
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.
|
||||
Parity-Ethereum v2.5.5-stable is a minor release that improves performance and stability.
|
||||
This release stabilises the 2.5 branch.
|
||||
|
||||
As of today, Parity-Ethereum 2.4 reaches end of life, and everyone is
|
||||
encouraged to upgrade.
|
||||
As of today, Parity-Ethereum 2.4 reaches end of life and everyone is
|
||||
encouraged to upgrade.
|
||||
|
||||
## Parity-Ethereum [v2.5.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.4)
|
||||
|
||||
Parity Ethereum v2.5.4-beta is a security update that addresses servo/rust-smallvec#148
|
||||
|
||||
The full list of included changes:
|
||||
* 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))
|
||||
|
||||
* cargo update -p smallvec ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822))
|
||||
|
||||
## Parity-Ethereum [v2.5.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.3)
|
||||
|
||||
Parity-Ethereum 2.5.3-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
* EthereumClassic: activate the Atlantis Hardfork
|
||||
* Clique: fix time overflow
|
||||
* State tests: treat empty accounts the same as non-existant accounts (EIP 1052)
|
||||
* Networking: support discovery-only peers (geth bootnodes)
|
||||
* Snapshotting: fix unclean shutdown while snappshotting is under way
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/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)
|
||||
|
||||
1711
Cargo.lock
generated
1711
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
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.2"
|
||||
version = "2.5.7"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
@@ -18,7 +18,7 @@ num_cpus = "1.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "1.0"
|
||||
semver = "0.9"
|
||||
ansi_term = "0.11"
|
||||
ansi_term = "0.10"
|
||||
parking_lot = "0.7"
|
||||
regex = "1.0"
|
||||
atty = "0.2.8"
|
||||
@@ -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 = "12.0.0"
|
||||
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,7 +63,7 @@ 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.3"
|
||||
@@ -73,8 +73,6 @@ 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"] }
|
||||
@@ -119,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
|
||||
@@ -139,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>
|
||||
|
||||
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,6 +8,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common-types = { path = "../ethcore/types" }
|
||||
ethkey = { path = "ethkey" }
|
||||
ethstore = { path = "ethstore" }
|
||||
log = "0.4"
|
||||
@@ -16,6 +17,12 @@ 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"
|
||||
@@ -17,12 +17,13 @@ tiny-keccak = "1.4"
|
||||
time = "0.1.34"
|
||||
itertools = "0.5"
|
||||
parking_lot = "0.7"
|
||||
parity-crypto = "0.4.0"
|
||||
ethereum-types = "0.6.0"
|
||||
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"
|
||||
|
||||
@@ -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,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"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
@@ -15,7 +15,7 @@ serde_json = "1.0"
|
||||
url = "1.2.0"
|
||||
matches = "0.1"
|
||||
parking_lot = "0.7"
|
||||
jsonrpc-core = "12.0.0"
|
||||
jsonrpc-ws-server = "12.0.0"
|
||||
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,8 +7,8 @@ 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.7"
|
||||
|
||||
@@ -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,17 +7,17 @@ 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" }
|
||||
@@ -26,16 +26,15 @@ 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"
|
||||
@@ -46,26 +45,24 @@ 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.4.0"
|
||||
parity-crypto = "0.3.0"
|
||||
parity-snappy = "0.1"
|
||||
parking_lot = "0.7"
|
||||
pod-account = { path = "pod-account" }
|
||||
trie-db = "0.12.4"
|
||||
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.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" }
|
||||
@@ -73,7 +70,6 @@ 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" }
|
||||
@@ -84,9 +80,8 @@ fetch = { path = "../util/fetch" }
|
||||
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"]
|
||||
|
||||
@@ -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"
|
||||
@@ -28,13 +28,13 @@ extern crate rustc_hex;
|
||||
use criterion::{Criterion, Bencher};
|
||||
use bytes::BytesRef;
|
||||
use ethcore::builtin::Builtin;
|
||||
use ethcore::machine::Machine;
|
||||
use ethereum_types::H160;
|
||||
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> {
|
||||
@@ -46,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();
|
||||
|
||||
@@ -57,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()];
|
||||
|
||||
|
||||
@@ -8,26 +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"
|
||||
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.7"
|
||||
rayon = "1.1"
|
||||
rlp = "0.4.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_compress = { path = "../../util/rlp-compress" }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
keccak-hash = "0.2.0"
|
||||
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.
|
||||
@@ -228,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>>,
|
||||
|
||||
@@ -284,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());
|
||||
@@ -314,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());
|
||||
@@ -469,7 +469,7 @@ impl<'a> Iterator for AncestryWithMetadataIter<'a> {
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
self.current = H256::zero();
|
||||
self.current = H256::default();
|
||||
None
|
||||
},
|
||||
}
|
||||
@@ -481,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> {
|
||||
@@ -521,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);
|
||||
|
||||
@@ -572,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
|
||||
}
|
||||
@@ -639,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);
|
||||
}
|
||||
@@ -652,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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -785,8 +788,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 maybe_parent = self.block_details(&block_parent_hash);
|
||||
|
||||
@@ -929,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,
|
||||
@@ -960,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,
|
||||
@@ -986,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) {
|
||||
@@ -1000,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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,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);
|
||||
|
||||
@@ -1183,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(),
|
||||
@@ -1270,7 +1259,7 @@ impl BlockChain {
|
||||
current: if self.is_known(&first) {
|
||||
first
|
||||
} else {
|
||||
H256::zero() // zero hash
|
||||
H256::default() // zero hash
|
||||
},
|
||||
chain: self
|
||||
}
|
||||
@@ -1486,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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1526,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()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1584,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
|
||||
}
|
||||
|
||||
@@ -1609,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();
|
||||
|
||||
@@ -1624,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();
|
||||
@@ -1643,6 +1629,8 @@ 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();
|
||||
@@ -2069,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());
|
||||
@@ -2142,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![
|
||||
@@ -2151,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![
|
||||
@@ -2160,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![
|
||||
@@ -2170,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![
|
||||
@@ -2249,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 {
|
||||
@@ -2317,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.7"
|
||||
rlp = "0.4.0"
|
||||
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
|
||||
@@ -189,12 +189,12 @@ pub trait Readable {
|
||||
}
|
||||
|
||||
/// 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();
|
||||
@@ -208,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,14 +7,15 @@ 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"
|
||||
keccak-hash = "0.1"
|
||||
parking_lot = "0.7"
|
||||
memory-cache = { path = "../../util/memory-cache" }
|
||||
num-bigint = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-hex = "1.0"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,12 +121,12 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
Request::Gas(Gas::from(1))
|
||||
},
|
||||
instructions::SSTORE => {
|
||||
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>,
|
||||
@@ -298,7 +316,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
} else if self.reader.len() == 0 {
|
||||
InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_ref().expect("Gasometer None case is checked above; qed").current_gas.as_u256())))
|
||||
} else {
|
||||
self.step_inner(ext)
|
||||
self.step_inner(ext).err().expect("step_inner never returns Ok(()); qed")
|
||||
};
|
||||
|
||||
if let &InterpreterResult::Done(_) = &result {
|
||||
@@ -310,7 +328,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
|
||||
/// 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 => {
|
||||
@@ -325,28 +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) {
|
||||
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;
|
||||
@@ -355,19 +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) => 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 {
|
||||
@@ -389,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<()> {
|
||||
@@ -512,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"),
|
||||
};
|
||||
|
||||
@@ -669,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))?;
|
||||
},
|
||||
@@ -706,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() {
|
||||
@@ -728,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));
|
||||
@@ -789,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]));
|
||||
@@ -823,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()));
|
||||
@@ -1013,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()
|
||||
});
|
||||
@@ -1029,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()
|
||||
});
|
||||
@@ -1174,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)]
|
||||
@@ -1191,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())
|
||||
@@ -1202,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 = {
|
||||
@@ -1225,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;
|
||||
|
||||
@@ -239,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}
|
||||
@@ -726,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));
|
||||
@@ -772,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));
|
||||
@@ -1066,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.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>,
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
use std::sync::{Weak, Arc};
|
||||
|
||||
use ethcore::client::{ClientReport, EnvInfo, ClientIoMessage};
|
||||
use ethcore::engines::{epoch, Engine, EpochChange, EpochTransition, Proof};
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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.7"
|
||||
ethabi = "8.0"
|
||||
ethabi-derive = "8.0"
|
||||
ethabi-contract = "8.0"
|
||||
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));
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
[package]
|
||||
description = "Account system expressed in Plain Old Data."
|
||||
name = "pod-account"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common-types = { path = "../types" }
|
||||
ethereum-types = "0.6"
|
||||
ethjson = { path = "../../json" }
|
||||
ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" }
|
||||
hash-db = "0.12"
|
||||
itertools = "0.8"
|
||||
keccak-hash = "0.2.0"
|
||||
keccak-hasher = { path = "../../util/keccak-hasher" }
|
||||
kvdb = "0.1"
|
||||
log = "0.4"
|
||||
parity-bytes = "0.1.0"
|
||||
rlp = "0.4"
|
||||
rustc-hex = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
trie-db = "0.12.4"
|
||||
triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" }
|
||||
|
||||
[dev-dependencies]
|
||||
macros = { path = "../../util/macros" }
|
||||
@@ -8,34 +8,33 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
common-types = { path = "../types" }
|
||||
derive_more = "0.14.0"
|
||||
ethabi = "8.0"
|
||||
ethabi-contract = "8.0"
|
||||
ethabi-derive = "8.0"
|
||||
ethabi = "6.0"
|
||||
ethabi-contract = "6.0"
|
||||
ethabi-derive = "6.0"
|
||||
ethcore = { path = ".." }
|
||||
ethcore-call-contract = { path = "../call-contract" }
|
||||
ethcore-io = { path = "../../util/io" }
|
||||
ethcore-miner = { path = "../../miner" }
|
||||
ethereum-types = "0.6.0"
|
||||
ethereum-types = "0.4"
|
||||
ethjson = { path = "../../json" }
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
fetch = { path = "../../util/fetch" }
|
||||
futures = "0.1"
|
||||
parity-util-mem = "0.1"
|
||||
keccak-hash = "0.2.0"
|
||||
heapsize = "0.4"
|
||||
keccak-hash = "0.1.2"
|
||||
log = "0.4"
|
||||
parity-bytes = "0.1"
|
||||
parity-crypto = "0.4.0"
|
||||
parity-crypto = "0.3.0"
|
||||
parking_lot = "0.7"
|
||||
trie-db = "0.12.4"
|
||||
trie-db = "0.11.0"
|
||||
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
|
||||
rand = "0.3"
|
||||
rlp = "0.4.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
time-utils = { path = "../../util/time-utils" }
|
||||
tiny-keccak = "1.4"
|
||||
transaction-pool = "2.0"
|
||||
url = "1"
|
||||
|
||||
@@ -114,7 +114,7 @@ impl SecretStoreEncryptor {
|
||||
let requester = self.config.key_server_account.ok_or_else(|| Error::KeyServerAccountNotSet)?;
|
||||
|
||||
// key id in SS is H256 && we have H160 here => expand with assitional zeros
|
||||
let contract_address_extended: H256 = (*contract_address).into();
|
||||
let contract_address_extended: H256 = contract_address.into();
|
||||
let base_url = self.config.base_url.clone().ok_or_else(|| Error::KeyServerNotSet)?;
|
||||
|
||||
// prepare request url
|
||||
@@ -156,7 +156,7 @@ impl SecretStoreEncryptor {
|
||||
let decrypted_key = Public::from_slice(&decrypted_bytes);
|
||||
|
||||
// and now take x coordinate of Public as a key
|
||||
let key: Bytes = decrypted_key.as_bytes()[..INIT_VEC_LEN].into();
|
||||
let key: Bytes = (*decrypted_key)[..INIT_VEC_LEN].into();
|
||||
|
||||
// cache the key in the session and clear expired sessions
|
||||
self.sessions.lock().insert(*contract_address, EncryptionSession{
|
||||
@@ -212,11 +212,11 @@ impl Encryptor for SecretStoreEncryptor {
|
||||
}?;
|
||||
|
||||
// encrypt data
|
||||
let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.as_bytes().len());
|
||||
let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.len());
|
||||
cypher.extend(repeat(0).take(plain_data.len()));
|
||||
crypto::aes::encrypt_128_ctr(&key, initialisation_vector.as_bytes(), plain_data, &mut cypher)
|
||||
crypto::aes::encrypt_128_ctr(&key, initialisation_vector, plain_data, &mut cypher)
|
||||
.map_err(|e| Error::Encrypt(e.to_string()))?;
|
||||
cypher.extend_from_slice(&initialisation_vector.as_bytes());
|
||||
cypher.extend_from_slice(&initialisation_vector);
|
||||
|
||||
Ok(cypher)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ use ethkey::Error as KeyError;
|
||||
use ethkey::crypto::Error as CryptoError;
|
||||
use txpool::VerifiedTransaction;
|
||||
use private_transactions::VerifiedPrivateTransaction;
|
||||
use serde_json::{Error as SerdeError};
|
||||
|
||||
type TxPoolError = txpool::Error<<VerifiedPrivateTransaction as VerifiedTransaction>::Hash>;
|
||||
|
||||
@@ -46,9 +45,6 @@ pub enum Error {
|
||||
/// Crypto error.
|
||||
#[display(fmt = "Crypto Error {}", _0)]
|
||||
Crypto(CryptoError),
|
||||
/// Serialization error.
|
||||
#[display(fmt = "Serialization Error {}", _0)]
|
||||
Json(SerdeError),
|
||||
/// Encryption error.
|
||||
#[display(fmt = "Encryption error. ({})", _0)]
|
||||
Encrypt(String),
|
||||
@@ -103,15 +99,6 @@ pub enum Error {
|
||||
/// Key server URL is not set.
|
||||
#[display(fmt = "Key server URL is not set.")]
|
||||
KeyServerNotSet,
|
||||
/// Transaction not found in logs.
|
||||
#[display(fmt = "Private transaction not found in logs.")]
|
||||
TxNotFoundInLog,
|
||||
/// Path for logging not set.
|
||||
#[display(fmt = "Path for logging not set.")]
|
||||
LoggingPathNotSet,
|
||||
/// Timestamp overflow error.
|
||||
#[display(fmt = "Timestamp overflow error.")]
|
||||
TimestampOverflow,
|
||||
/// VM execution error.
|
||||
#[display(fmt = "VM execution error {}", _0)]
|
||||
Execution(ExecutionError),
|
||||
@@ -136,7 +123,6 @@ impl error::Error for Error {
|
||||
Error::Decoder(e) => Some(e),
|
||||
Error::Trie(e) => Some(e),
|
||||
Error::TxPool(e) => Some(e),
|
||||
Error::Json(e) => Some(e),
|
||||
Error::Crypto(e) => Some(e),
|
||||
Error::Execution(e) => Some(e),
|
||||
Error::Key(e) => Some(e),
|
||||
@@ -201,12 +187,6 @@ impl From<TxPoolError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SerdeError> for Error {
|
||||
fn from(err: SerdeError) -> Self {
|
||||
Error::Json(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthcoreError> for Error {
|
||||
fn from(err: EthcoreError) -> Self {
|
||||
Error::Ethcore(err).into()
|
||||
|
||||
@@ -29,15 +29,15 @@ use_contract!(keys_acl_contract, "res/keys_acl.json");
|
||||
|
||||
/// Returns the address (of the contract), that corresponds to the key
|
||||
pub fn key_to_address(key: &H256) -> Address {
|
||||
Address::from_slice(&key.as_bytes()[..10])
|
||||
Address::from_slice(&key.to_vec()[..10])
|
||||
}
|
||||
|
||||
/// Returns the key from the key server associated with the contract
|
||||
pub fn address_to_key(contract_address: &Address) -> H256 {
|
||||
// Current solution uses contract address extended with 0 as id
|
||||
let contract_address_extended: H256 = (*contract_address).into();
|
||||
let contract_address_extended: H256 = contract_address.into();
|
||||
|
||||
H256::from_slice(contract_address_extended.as_bytes())
|
||||
H256::from_slice(&contract_address_extended)
|
||||
}
|
||||
|
||||
/// Trait for keys server keys provider.
|
||||
@@ -118,7 +118,7 @@ impl Default for StoringKeyProvider {
|
||||
fn default() -> Self {
|
||||
StoringKeyProvider {
|
||||
available_keys: RwLock::new(None),
|
||||
key_server_account: Some(Address::zero()),
|
||||
key_server_account: Some(Address::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,4 +170,4 @@ mod tests {
|
||||
keys_data.update_acl_contract();
|
||||
assert_eq!(keys_data.keys_acl_contract.read().unwrap(), key.address());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,15 @@
|
||||
|
||||
//! Private transactions module.
|
||||
|
||||
// Recursion limit required because of
|
||||
// error_chain foreign_links.
|
||||
#![recursion_limit="256"]
|
||||
|
||||
mod encryptor;
|
||||
mod key_server_keys;
|
||||
mod private_transactions;
|
||||
mod messages;
|
||||
mod error;
|
||||
mod log;
|
||||
|
||||
extern crate common_types as types;
|
||||
extern crate ethabi;
|
||||
@@ -34,7 +37,7 @@ extern crate ethjson;
|
||||
extern crate ethkey;
|
||||
extern crate fetch;
|
||||
extern crate futures;
|
||||
extern crate parity_util_mem;
|
||||
extern crate heapsize;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate parity_crypto as crypto;
|
||||
@@ -42,15 +45,11 @@ extern crate parking_lot;
|
||||
extern crate trie_db as trie;
|
||||
extern crate patricia_trie_ethereum as ethtrie;
|
||||
extern crate rlp;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate rustc_hex;
|
||||
extern crate transaction_pool as txpool;
|
||||
extern crate url;
|
||||
#[macro_use]
|
||||
extern crate log as ethlog;
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate ethabi_derive;
|
||||
#[macro_use]
|
||||
@@ -59,9 +58,6 @@ extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate rlp_derive;
|
||||
|
||||
#[cfg(not(time_checked_add))]
|
||||
extern crate time_utils;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
#[cfg(test)]
|
||||
@@ -72,11 +68,10 @@ pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider};
|
||||
pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore};
|
||||
pub use messages::{PrivateTransaction, SignedPrivateTransaction};
|
||||
pub use error::Error;
|
||||
pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer};
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::collections::{HashMap, HashSet, BTreeMap};
|
||||
use ethereum_types::{H128, H256, U256, Address, BigEndianHash};
|
||||
use ethereum_types::{H128, H256, U256, Address};
|
||||
use hash::keccak;
|
||||
use rlp::*;
|
||||
use parking_lot::RwLock;
|
||||
@@ -122,8 +117,6 @@ pub struct ProviderConfig {
|
||||
pub validator_accounts: Vec<Address>,
|
||||
/// Account used for signing public transactions created from private transactions
|
||||
pub signer_account: Option<Address>,
|
||||
/// Path to private tx logs
|
||||
pub logs_path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -184,7 +177,6 @@ pub struct Provider {
|
||||
accounts: Arc<Signer>,
|
||||
channel: IoChannel<ClientIoMessage>,
|
||||
keys_provider: Arc<KeyProvider>,
|
||||
logging: Option<Logging>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -219,7 +211,6 @@ impl Provider {
|
||||
accounts,
|
||||
channel,
|
||||
keys_provider,
|
||||
logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,11 +257,8 @@ impl Provider {
|
||||
trace!(target: "privatetx", "Required validators: {:?}", contract_validators);
|
||||
let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce);
|
||||
trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash);
|
||||
self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, &contract_validators, private_state, contract_nonce)?;
|
||||
self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?;
|
||||
self.broadcast_private_transaction(private.hash(), private.rlp_bytes());
|
||||
if let Some(ref logging) = self.logging {
|
||||
logging.private_tx_created(&tx_hash, &contract_validators);
|
||||
}
|
||||
Ok(Receipt {
|
||||
hash: tx_hash,
|
||||
contract_address: contract,
|
||||
@@ -281,10 +269,9 @@ impl Provider {
|
||||
/// Calculate hash from united private state and contract nonce
|
||||
pub fn calculate_state_hash(&self, state: &Bytes, nonce: U256) -> H256 {
|
||||
let state_hash = keccak(state);
|
||||
let nonce_h256: H256 = BigEndianHash::from_uint(&nonce);
|
||||
let mut state_buf = [0u8; 64];
|
||||
state_buf[..32].clone_from_slice(state_hash.as_bytes());
|
||||
state_buf[32..].clone_from_slice(nonce_h256.as_bytes());
|
||||
state_buf[..32].clone_from_slice(&state_hash);
|
||||
state_buf[32..].clone_from_slice(&H256::from(nonce));
|
||||
keccak(&state_buf.as_ref())
|
||||
}
|
||||
|
||||
@@ -367,9 +354,8 @@ impl Provider {
|
||||
Some(desc) => desc,
|
||||
};
|
||||
let last = self.last_required_signature(&desc, signed_tx.signature())?;
|
||||
let original_tx_hash = desc.original_transaction.hash();
|
||||
|
||||
if last.0 {
|
||||
if last {
|
||||
let mut signatures = desc.received_signatures.clone();
|
||||
signatures.push(signed_tx.signature());
|
||||
let rsv: Vec<Signature> = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect();
|
||||
@@ -387,8 +373,8 @@ impl Provider {
|
||||
trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx);
|
||||
// Sign and add it to the queue
|
||||
let chain_id = desc.original_transaction.chain_id();
|
||||
let public_tx_hash = public_tx.hash(chain_id);
|
||||
let signature = self.accounts.sign(signer_account, public_tx_hash)?;
|
||||
let hash = public_tx.hash(chain_id);
|
||||
let signature = self.accounts.sign(signer_account, hash)?;
|
||||
let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?;
|
||||
match self.miner.import_own_transaction(&*self.client, signed.into()) {
|
||||
Ok(_) => trace!(target: "privatetx", "Public transaction added to queue"),
|
||||
@@ -406,11 +392,6 @@ impl Provider {
|
||||
Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err),
|
||||
}
|
||||
}
|
||||
// Store logs
|
||||
if let Some(ref logging) = self.logging {
|
||||
logging.signature_added(&original_tx_hash, &last.1);
|
||||
logging.tx_deployed(&original_tx_hash, &public_tx_hash);
|
||||
}
|
||||
// Remove from store for signing
|
||||
if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) {
|
||||
warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err);
|
||||
@@ -419,12 +400,7 @@ impl Provider {
|
||||
} else {
|
||||
// Add signature to the store
|
||||
match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) {
|
||||
Ok(_) => {
|
||||
trace!(target: "privatetx", "Signature stored for private transaction");
|
||||
if let Some(ref logging) = self.logging {
|
||||
logging.signature_added(&original_tx_hash, &last.1);
|
||||
}
|
||||
}
|
||||
Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"),
|
||||
Err(err) => {
|
||||
warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err);
|
||||
return Err(err);
|
||||
@@ -444,14 +420,17 @@ impl Provider {
|
||||
}
|
||||
}
|
||||
|
||||
fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<(bool, Address), Error> {
|
||||
fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<bool, Error> {
|
||||
if desc.received_signatures.contains(&sign) {
|
||||
return Ok(false);
|
||||
}
|
||||
let state_hash = self.calculate_state_hash(&desc.state, desc.contract_nonce);
|
||||
match recover(&sign, &state_hash) {
|
||||
Ok(public) => {
|
||||
let sender = public_to_address(&public);
|
||||
match desc.validators.contains(&sender) {
|
||||
true => {
|
||||
Ok((desc.received_signatures.len() + 1 == desc.validators.len(), sender))
|
||||
Ok(desc.received_signatures.len() + 1 == desc.validators.len())
|
||||
}
|
||||
false => {
|
||||
warn!(target: "privatetx", "Sender's state doesn't correspond to validator's");
|
||||
@@ -478,13 +457,13 @@ impl Provider {
|
||||
|
||||
fn iv_from_transaction(transaction: &SignedTransaction) -> H128 {
|
||||
let nonce = keccak(&transaction.nonce.rlp_bytes());
|
||||
let (iv, _) = nonce.as_bytes().split_at(INIT_VEC_LEN);
|
||||
let (iv, _) = nonce.split_at(INIT_VEC_LEN);
|
||||
H128::from_slice(iv)
|
||||
}
|
||||
|
||||
fn iv_from_address(contract_address: &Address) -> H128 {
|
||||
let address = keccak(&contract_address.rlp_bytes());
|
||||
let (iv, _) = address.as_bytes().split_at(INIT_VEC_LEN);
|
||||
let (iv, _) = address.split_at(INIT_VEC_LEN);
|
||||
H128::from_slice(iv)
|
||||
}
|
||||
|
||||
@@ -533,8 +512,8 @@ impl Provider {
|
||||
// Sort the storage to guarantee the order for all parties
|
||||
let sorted_storage: BTreeMap<&H256, &H256> = storage.iter().collect();
|
||||
for (key, value) in sorted_storage {
|
||||
raw.extend_from_slice(key.as_bytes());
|
||||
raw.extend_from_slice(value.as_bytes());
|
||||
raw.extend_from_slice(key);
|
||||
raw.extend_from_slice(value);
|
||||
};
|
||||
raw
|
||||
}
|
||||
@@ -618,8 +597,8 @@ impl Provider {
|
||||
v[31] = s.v();
|
||||
v
|
||||
}).collect::<Vec<[u8; 32]>>(),
|
||||
signatures.iter().map(|s| H256::from_slice(s.r())).collect::<Vec<H256>>(),
|
||||
signatures.iter().map(|s| H256::from_slice(s.s())).collect::<Vec<H256>>(),
|
||||
signatures.iter().map(|s| s.r()).collect::<Vec<&[u8]>>(),
|
||||
signatures.iter().map(|s| s.s()).collect::<Vec<&[u8]>>()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -695,14 +674,6 @@ impl Provider {
|
||||
Ok(result.result)
|
||||
}
|
||||
|
||||
/// Retrieves log information about private transaction
|
||||
pub fn private_log(&self, tx_hash: H256) -> Result<TransactionLog, Error> {
|
||||
match self.logging {
|
||||
Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog),
|
||||
None => Err(Error::LoggingPathNotSet),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns private validators for a contract.
|
||||
pub fn get_validators(&self, block: BlockId, address: &Address) -> Result<Vec<Address>, Error> {
|
||||
let (data, decoder) = private_contract::functions::get_validators::call();
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Private transactions logs.
|
||||
|
||||
use ethereum_types::{H256, Address};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, Duration, Instant};
|
||||
use parking_lot::RwLock;
|
||||
use serde::ser::{Serializer, SerializeSeq};
|
||||
use error::Error;
|
||||
|
||||
#[cfg(not(time_checked_add))]
|
||||
use time_utils::CheckedSystemTime;
|
||||
|
||||
/// Maximum amount of stored private transaction logs.
|
||||
const MAX_JOURNAL_LEN: usize = 1000;
|
||||
|
||||
/// Maximum period for storing private transaction logs.
|
||||
/// Logs older than 20 days will not be processed
|
||||
const MAX_STORING_TIME: Duration = Duration::from_secs(60 * 60 * 24 * 20);
|
||||
|
||||
/// Source of monotonic time for log timestamps
|
||||
struct MonoTime {
|
||||
start_time: SystemTime,
|
||||
start_inst: Instant
|
||||
}
|
||||
|
||||
impl MonoTime {
|
||||
fn new(start: SystemTime) -> Self {
|
||||
Self {
|
||||
start_time: start,
|
||||
start_inst: Instant::now()
|
||||
}
|
||||
}
|
||||
|
||||
fn elapsed(&self) -> Duration {
|
||||
self.start_inst.elapsed()
|
||||
}
|
||||
|
||||
fn to_system_time(&self) -> SystemTime {
|
||||
self.start_time + self.elapsed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MonoTime {
|
||||
fn default() -> Self {
|
||||
MonoTime::new(SystemTime::now())
|
||||
}
|
||||
}
|
||||
|
||||
/// Current status of the private transaction
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub enum PrivateTxStatus {
|
||||
/// Private tx was created but no validation received yet
|
||||
Created,
|
||||
/// Several validators (but not all) validated the transaction
|
||||
Validating,
|
||||
/// All validators has validated the private tx
|
||||
/// Corresponding public tx was created and added into the pool
|
||||
Deployed,
|
||||
}
|
||||
|
||||
/// Information about private tx validation
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ValidatorLog {
|
||||
/// Account of the validator
|
||||
pub account: Address,
|
||||
/// Validation timestamp, None if the transaction is not validated
|
||||
pub validation_timestamp: Option<SystemTime>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl PartialEq for ValidatorLog {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.account == other.account
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the private transaction
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TransactionLog {
|
||||
/// Original signed transaction hash (used as a source for private tx)
|
||||
pub tx_hash: H256,
|
||||
/// Current status of the private transaction
|
||||
pub status: PrivateTxStatus,
|
||||
/// Creation timestamp
|
||||
pub creation_timestamp: SystemTime,
|
||||
/// List of validations
|
||||
pub validators: Vec<ValidatorLog>,
|
||||
/// Timestamp of the resulting public tx deployment
|
||||
pub deployment_timestamp: Option<SystemTime>,
|
||||
/// Hash of the resulting public tx
|
||||
pub public_tx_hash: Option<H256>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl PartialEq for TransactionLog {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.tx_hash == other.tx_hash &&
|
||||
self.status == other.status &&
|
||||
self.validators == other.validators &&
|
||||
self.public_tx_hash == other.public_tx_hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper other JSON serializer
|
||||
pub trait LogsSerializer: Send + Sync + 'static {
|
||||
/// Read logs from the source
|
||||
fn read_logs(&self) -> Result<Vec<TransactionLog>, Error>;
|
||||
|
||||
/// Write all logs to the source
|
||||
fn flush_logs(&self, logs: &HashMap<H256, TransactionLog>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Logs serializer to the json file
|
||||
pub struct FileLogsSerializer {
|
||||
logs_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl FileLogsSerializer {
|
||||
pub fn with_path<P: Into<PathBuf>>(logs_dir: P) -> Self {
|
||||
FileLogsSerializer {
|
||||
logs_dir: logs_dir.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn open_file(&self, to_create: bool) -> Result<File, Error> {
|
||||
let file_path = self.logs_dir.with_file_name("private_tx.log");
|
||||
if to_create {
|
||||
File::create(&file_path).map_err(From::from)
|
||||
} else {
|
||||
File::open(&file_path).map_err(From::from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LogsSerializer for FileLogsSerializer {
|
||||
fn read_logs(&self) -> Result<Vec<TransactionLog>, Error> {
|
||||
let log_file = self.open_file(false)?;
|
||||
match serde_json::from_reader(log_file) {
|
||||
Ok(logs) => Ok(logs),
|
||||
Err(err) => {
|
||||
error!(target: "privatetx", "Cannot deserialize logs from file: {}", err);
|
||||
return Err(format!("Cannot deserialize logs from file: {:?}", err).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_logs(&self, logs: &HashMap<H256, TransactionLog>) -> Result<(), Error> {
|
||||
if logs.is_empty() {
|
||||
// Do not create empty file
|
||||
return Ok(());
|
||||
}
|
||||
let log_file = self.open_file(true)?;
|
||||
let mut json = serde_json::Serializer::new(log_file);
|
||||
let mut json_array = json.serialize_seq(Some(logs.len()))?;
|
||||
for v in logs.values() {
|
||||
json_array.serialize_element(v)?;
|
||||
}
|
||||
json_array.end()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Private transactions logging
|
||||
pub struct Logging {
|
||||
logs: RwLock<HashMap<H256, TransactionLog>>,
|
||||
logs_serializer: Arc<LogsSerializer>,
|
||||
mono_time: MonoTime,
|
||||
}
|
||||
|
||||
impl Logging {
|
||||
/// Creates the logging object
|
||||
pub fn new(logs_serializer: Arc<LogsSerializer>) -> Self {
|
||||
let mut logging = Logging {
|
||||
logs: RwLock::new(HashMap::new()),
|
||||
logs_serializer,
|
||||
mono_time: MonoTime::default(),
|
||||
};
|
||||
match logging.read_logs() {
|
||||
// Initialize time source by max from current system time and max creation time from already saved logs
|
||||
Ok(initial_time) => logging.mono_time = MonoTime::new(initial_time),
|
||||
Err(err) => warn!(target: "privatetx", "Cannot read logs: {:?}", err),
|
||||
}
|
||||
logging
|
||||
}
|
||||
|
||||
/// Retrieves log for the corresponding tx hash
|
||||
pub fn tx_log(&self, tx_hash: &H256) -> Option<TransactionLog> {
|
||||
self.logs.read().get(&tx_hash).cloned()
|
||||
}
|
||||
|
||||
/// Logs the creation of the private transaction
|
||||
pub fn private_tx_created(&self, tx_hash: &H256, validators: &[Address]) {
|
||||
let mut validator_logs = Vec::new();
|
||||
for account in validators {
|
||||
validator_logs.push(ValidatorLog {
|
||||
account: *account,
|
||||
validation_timestamp: None,
|
||||
});
|
||||
}
|
||||
let mut logs = self.logs.write();
|
||||
if logs.len() > MAX_JOURNAL_LEN {
|
||||
// Remove the oldest log
|
||||
if let Some(tx_hash) = logs.values()
|
||||
.min_by(|x, y| x.creation_timestamp.cmp(&y.creation_timestamp))
|
||||
.map(|oldest| oldest.tx_hash)
|
||||
{
|
||||
logs.remove(&tx_hash);
|
||||
}
|
||||
}
|
||||
logs.insert(*tx_hash, TransactionLog {
|
||||
tx_hash: *tx_hash,
|
||||
status: PrivateTxStatus::Created,
|
||||
creation_timestamp: self.mono_time.to_system_time(),
|
||||
validators: validator_logs,
|
||||
deployment_timestamp: None,
|
||||
public_tx_hash: None,
|
||||
});
|
||||
}
|
||||
|
||||
/// Logs the validation of the private transaction by one of its validators
|
||||
pub fn signature_added(&self, tx_hash: &H256, validator: &Address) {
|
||||
let mut logs = self.logs.write();
|
||||
if let Some(transaction_log) = logs.get_mut(&tx_hash) {
|
||||
if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) {
|
||||
transaction_log.status = PrivateTxStatus::Validating;
|
||||
validator_log.validation_timestamp = Some(self.mono_time.to_system_time());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs the final deployment of the resulting public transaction
|
||||
pub fn tx_deployed(&self, tx_hash: &H256, public_tx_hash: &H256) {
|
||||
let mut logs = self.logs.write();
|
||||
if let Some(log) = logs.get_mut(&tx_hash) {
|
||||
log.status = PrivateTxStatus::Deployed;
|
||||
log.deployment_timestamp = Some(self.mono_time.to_system_time());
|
||||
log.public_tx_hash = Some(*public_tx_hash);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_logs(&self) -> Result<SystemTime, Error> {
|
||||
let mut transaction_logs = self.logs_serializer.read_logs()?;
|
||||
// Drop old logs
|
||||
let earliest_possible = SystemTime::now().checked_sub(MAX_STORING_TIME).ok_or(Error::TimestampOverflow)?;
|
||||
transaction_logs.retain(|tx_log| tx_log.creation_timestamp > earliest_possible);
|
||||
// Sort logs by their creation time in order to find the most recent
|
||||
transaction_logs.sort_by(|a, b| b.creation_timestamp.cmp(&a.creation_timestamp));
|
||||
let initial_timestamp = transaction_logs.first()
|
||||
.map_or(SystemTime::now(), |l| std::cmp::max(SystemTime::now(), l.creation_timestamp));
|
||||
let mut logs = self.logs.write();
|
||||
for log in transaction_logs {
|
||||
logs.insert(log.tx_hash, log);
|
||||
}
|
||||
Ok(initial_timestamp)
|
||||
}
|
||||
|
||||
fn flush_logs(&self) -> Result<(), Error> {
|
||||
let logs = self.logs.read();
|
||||
self.logs_serializer.flush_logs(&logs)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all logs on drop
|
||||
impl Drop for Logging {
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = self.flush_logs() {
|
||||
warn!(target: "privatetx", "Cannot write logs: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use error::Error;
|
||||
use ethereum_types::{H256, Address};
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use std::str::FromStr;
|
||||
use types::transaction::Transaction;
|
||||
use parking_lot::RwLock;
|
||||
use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, ValidatorLog};
|
||||
|
||||
#[cfg(not(time_checked_add))]
|
||||
use time_utils::CheckedSystemTime;
|
||||
|
||||
struct StringLogSerializer {
|
||||
string_log: RwLock<String>,
|
||||
}
|
||||
|
||||
impl StringLogSerializer {
|
||||
fn new(source: String) -> Self {
|
||||
StringLogSerializer {
|
||||
string_log: RwLock::new(source),
|
||||
}
|
||||
}
|
||||
|
||||
fn log(&self) -> String {
|
||||
let log = self.string_log.read();
|
||||
log.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl LogsSerializer for StringLogSerializer {
|
||||
fn read_logs(&self) -> Result<Vec<TransactionLog>, Error> {
|
||||
let source = self.string_log.read();
|
||||
if source.is_empty() {
|
||||
return Ok(Vec::new())
|
||||
}
|
||||
let logs = serde_json::from_str(&source).unwrap();
|
||||
Ok(logs)
|
||||
}
|
||||
|
||||
fn flush_logs(&self, logs: &HashMap<H256, TransactionLog>) -> Result<(), Error> {
|
||||
// Sort logs in order to have the same order
|
||||
let sorted_logs: BTreeMap<&H256, &TransactionLog> = logs.iter().collect();
|
||||
*self.string_log.write() = serde_json::to_string(&sorted_logs.values().collect::<Vec<&&TransactionLog>>())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_log_format() {
|
||||
let s = r#"{
|
||||
"tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6",
|
||||
"status":"Deployed",
|
||||
"creation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053},
|
||||
"validators":[{
|
||||
"account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1",
|
||||
"validation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}
|
||||
}],
|
||||
"deployment_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053},
|
||||
"public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da"
|
||||
}"#;
|
||||
|
||||
let _deserialized: TransactionLog = serde_json::from_str(s).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_log_status() {
|
||||
let logger = Logging::new(Arc::new(StringLogSerializer::new("".into())));
|
||||
let private_tx = Transaction::default();
|
||||
let hash = private_tx.hash(None);
|
||||
logger.private_tx_created(&hash, &vec![Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()]);
|
||||
logger.signature_added(&hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap());
|
||||
logger.tx_deployed(&hash, &hash);
|
||||
let tx_log = logger.tx_log(&hash).unwrap();
|
||||
assert_eq!(tx_log.status, PrivateTxStatus::Deployed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let current_timestamp = SystemTime::now();
|
||||
let initial_validator_log = ValidatorLog {
|
||||
account: Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap(),
|
||||
validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(1)).unwrap()),
|
||||
};
|
||||
let initial_log = TransactionLog {
|
||||
tx_hash: H256::from_str("64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6").unwrap(),
|
||||
status: PrivateTxStatus::Deployed,
|
||||
creation_timestamp: current_timestamp,
|
||||
validators: vec![initial_validator_log],
|
||||
deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(2)).unwrap()),
|
||||
public_tx_hash: Some(H256::from_str("69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da").unwrap()),
|
||||
};
|
||||
let serializer = Arc::new(StringLogSerializer::new(serde_json::to_string(&vec![initial_log.clone()]).unwrap()));
|
||||
let logger = Logging::new(serializer.clone());
|
||||
let hash = H256::from_str("63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6").unwrap();
|
||||
logger.private_tx_created(&hash, &vec![Address::from_str("7ffbe3512782069be388f41be4d8eb350672d3a5").unwrap()]);
|
||||
logger.signature_added(&hash, &Address::from_str("7ffbe3512782069be388f41be4d8eb350672d3a5").unwrap());
|
||||
logger.tx_deployed(&hash, &H256::from_str("de2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7").unwrap());
|
||||
drop(logger);
|
||||
let added_validator_log = ValidatorLog {
|
||||
account: Address::from_str("7ffbe3512782069be388f41be4d8eb350672d3a5").unwrap(),
|
||||
validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(7)).unwrap()),
|
||||
};
|
||||
let added_log = TransactionLog {
|
||||
tx_hash: H256::from_str("63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6").unwrap(),
|
||||
status: PrivateTxStatus::Deployed,
|
||||
creation_timestamp: current_timestamp.checked_add(Duration::from_secs(6)).unwrap(),
|
||||
validators: vec![added_validator_log],
|
||||
deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(8)).unwrap()),
|
||||
public_tx_hash: Some(H256::from_str("de2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7").unwrap()),
|
||||
};
|
||||
let should_be_final = vec![added_log, initial_log];
|
||||
let deserialized_logs: Vec<TransactionLog> = serde_json::from_str(&serializer.log()).unwrap();
|
||||
assert_eq!(deserialized_logs, should_be_final);
|
||||
}
|
||||
}
|
||||
@@ -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 ethereum_types::{H256, U256, Address, BigEndianHash};
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use bytes::Bytes;
|
||||
use hash::keccak;
|
||||
use rlp::Encodable;
|
||||
@@ -38,7 +38,7 @@ impl PrivateTransaction {
|
||||
PrivateTransaction {
|
||||
encrypted,
|
||||
contract,
|
||||
hash: H256::zero(),
|
||||
hash: 0.into(),
|
||||
}.compute_hash()
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl SignedPrivateTransaction {
|
||||
r: sig.r().into(),
|
||||
s: sig.s().into(),
|
||||
v: add_chain_replay_protection(sig.v() as u64, chain_id),
|
||||
hash: H256::zero(),
|
||||
hash: 0.into(),
|
||||
}.compute_hash()
|
||||
}
|
||||
|
||||
@@ -100,11 +100,7 @@ impl SignedPrivateTransaction {
|
||||
|
||||
/// Construct a signature object from the sig.
|
||||
pub fn signature(&self) -> Signature {
|
||||
Signature::from_rsv(
|
||||
&BigEndianHash::from_uint(&self.r),
|
||||
&BigEndianHash::from_uint(&self.s),
|
||||
self.standard_v(),
|
||||
)
|
||||
Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v())
|
||||
}
|
||||
|
||||
/// Get the hash of of the original transaction.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user