Compare commits

..

269 Commits
v2.7.2 ... main

Author SHA1 Message Date
POA 6e06824c23 Bump to v3.3.3 2022-01-13 12:34:02 +03:00
Rim Rakhimov 43d6da6b52
Made nodes data concatenate as RLP sequences instead of bytes (#598)
* Made nodes data concatenate as RLP sequences instead of bytes

* Add test for getNodeData dispatch

* Update getNodeData to return data for non-aura networks
2022-01-13 12:26:33 +03:00
Rim Rakhimov 412d797a3b
Implements eip-3607 (#593)
* implements eip-3607

* Tests fixed

* Added tests for eip3607 functionality

* rast fmt
2022-01-10 18:01:46 +03:00
Rim Rakhimov 63bab44e3c
Makes eth_mining to return False if not is not allowed to seal (#581) 2021-12-10 14:25:39 +03:00
Rim Rakhimov f703d01f23
Add type field for legacy transactions in RPC calls (#580)
* Added type field for legacy transactions when returned as rpc call response

* Remove invalid test
2021-12-09 11:17:42 +03:00
POA f13fa10b8a Bump to v3.3.2, London on Sokol, effective_gas_price function enhancement 2021-12-07 14:03:48 +03:00
POA 657100cebc Bump to v3.3.1 2021-11-30 10:55:54 +03:00
POA d2d19ec8c2 Merge branch 'dev' of https://github.com/openethereum/openethereum into dev 2021-11-30 10:39:06 +03:00
POA 0f872aff78 Add a bootnode for Kovan 2021-11-30 10:38:55 +03:00
Rim Rakhimov 405738c791
Fix for modexp overflow in debug mode (#578)
* fix modexp overflow panic in debug mode

* Added one more unit test for modexp

* Remove redundant bytes from modexp unit test

Co-authored-by: POA <33550681+poa@users.noreply.github.com>
2021-11-30 10:33:40 +03:00
Rim Rakhimov 3b19a79c37
Adds eth_maxPriorityFeePerGas implementaiton (#570)
* added eth_maxPriorityFeePerGas rpc call

* cargo fmt

* moved block_base_fee implementation into the trait

* added basic test for eth_maxPriorityFeePerGas

* added test for eth_maxPriorityFeePerGas calculation

* Added support for zero-cost transactions

* Added 'eip1559_not_activated' error

* Fixes 'chain::supplier::test::return_nodes' test

* cargo fmt

* cargo fmt

* made calculation of fallback priority fee to ignore zero-cost transactions

* cargo fmt

* made use of 'saturating_sub' instead of minus
2021-11-25 10:43:00 +03:00
POA 64a1614769 Bump to v3.3.0 2021-11-17 12:05:58 +03:00
POA b981f7beef Add validateServiceTransactionsTransition spec option 2021-11-12 13:06:19 +03:00
POA caa210107e Add additional service transactions checking to block verifier 2021-11-10 16:36:17 +03:00
POA 88eb7d3257 Revert eip1559BaseFeeMinValue activation on xDai 2021-11-04 15:50:48 +03:00
POA 73895aae88 Set eip1559BaseFeeMinValue for POA Core 2021-11-02 10:29:54 +03:00
POA c5719983b2 Set eip1559BaseFeeInitialValue equal to eip1559BaseFeeMinValue for xDai 2021-11-02 09:34:53 +03:00
POA f9b2db206a Replace eip1559BaseFeeFixedValue* spec options with eip1559BaseFeeMinValue and eip1559BaseFeeMinValueTransition 2021-11-01 13:20:58 +03:00
POA 437ba9b044 Add eip1559BaseFeeFixedValue and eip1559BaseFeeFixedValueTransition spec options and bump to v3.3.0-rc.14 2021-11-01 10:53:50 +03:00
uink45 98873fc0c0
EIP-4345 enabled (#561)
* EIP-4345 enabled

Co-authored-by: varasev <33550681+varasev@users.noreply.github.com>
2021-11-01 07:14:52 +03:00
POA 415a522429 POA Core London hard fork and bump to v3.3.0-rc.13 2021-10-29 16:37:12 +03:00
POA 646f49fdd0 xDai London hard fork and bump to v3.3.0-rc.12 2021-10-22 09:44:16 +03:00
POA 25ce4b2ec8 Enable GetNodeData for AuRa chains and bump to v3.3.0-rc.11 2021-10-19 11:25:32 +03:00
POA ac30783c82 Add eip1559FeeCollector and eip1559FeeCollectorTransition spec options and bump to v3.3.0-rc.10 2021-10-14 16:14:38 +03:00
POA 5e9b4c58ae Bump to v3.3.0-rc.9 2021-10-04 10:43:30 +03:00
POA 74e709fd55 Fix MinGasPrice config option 2021-09-29 10:30:28 +03:00
POA 3413343caa Add service transactions support for EIP-1559 2021-09-29 10:30:28 +03:00
sunce86 8bb02dd479 small fixes 2021-09-29 10:30:27 +03:00
sunce86 79be8f1ab8 min_gas_price becomes min_effective_priority_fee (effective_gas_price - base_fee) 2021-09-29 10:30:27 +03:00
sunce86 a049baf6b2 added version 4 for TxPermission contract 2021-09-29 10:30:27 +03:00
sunce86 d8305c52ea Bump to v3.3.0-rc.8 2021-09-03 17:07:40 +02:00
Jochen Müller 239d790df1
Ignore GetNodeData requests (#519) 2021-09-03 16:43:33 +02:00
Jochen Müller 298a1a0ecc Bump to v3.3.0-rc.7 2021-08-23 14:03:36 +02:00
Jochen Müller 36b3f125a4
Fix wrong sync packet id (#514) 2021-08-23 13:54:25 +02:00
sunce86 5d9ff63043 Bump to v3.3.0-rc.6 2021-08-04 18:15:06 +02:00
sunce86 01996c8867 Merge branch 'dev' 2021-08-04 18:05:31 +02:00
varasev 2ae294990a
London HF on Kovan (#502)
* London HF on Kovan

Block number 26741100

* Set wasmDisableTransition for Kovan

Co-authored-by: Dusan Stanivukovic <dusan.stanivukovic@gmail.com>
2021-08-03 14:02:37 +02:00
sunce86 745c4bd00c Revert "Sunce86/eip1559 for x dai (#499)"
This reverts commit 87ae05d99e.
2021-08-03 11:49:51 +02:00
sunce86 ecae5f1c47 Revert "Sunce86/eip1559 for x dai (#499)"
This reverts commit 87ae05d99e.
2021-08-03 11:47:01 +02:00
Dusan Stanivukovic 87ae05d99e
Sunce86/eip1559 for x dai (#499)
* added version 4 for TxPermission contract
* min_gas_price becomes min_effective_priority_fee (effective_gas_price - base_fee)
2021-08-02 19:47:39 +02:00
sunce86 a92e5e761b Bump to v3.3.0-rc.4 2021-07-14 21:46:58 +02:00
Dusan Stanivukovic 87603926b5
feeHistory implementation (#484)
* feeHistory implementation

* code review fix
2021-07-14 21:21:28 +02:00
Dusan Stanivukovic eb42f0c5d9
gasPrice is required field for Transaction object (#481) 2021-07-12 15:22:27 +02:00
Dusan Stanivukovic 33908e8361
fix state test for openethereum-evm (#480)
* fix state test for openethereum-evm
2021-07-10 08:53:53 +02:00
Dusan Stanivukovic aa09846200
bump ethereum tests 9.0.3 (#478) 2021-07-09 16:47:02 +02:00
Dusan Stanivukovic 67ab600bc9
Sunce86/effective gas price not omitting (#477)
* no omit effectiveGasPrice in receipts
2021-07-09 14:56:37 +02:00
Dusan Stanivukovic 4b437428bd
define london block at 12965000 (#476)
* define london block at 12965000
2021-07-08 22:55:53 +02:00
Jochen Müller 38e40f649c
Restore GetNodeData (#469)
* Accept GetNodeData requests

* Implement blockchain client method for node data requests

* Reuse old database read methods for node data

* fmt

* Copy & paste old tests...

* ... and make them work

* fmt
2021-07-05 17:06:35 +02:00
Jochen Müller 43ee520904
Implement eth/66 (#467)
* Allow eth/66

* Add eth/66 request ids

* fmt

* Remove some leftovers

* fmt

* Change behaviour in case of missing peer info

- Assume eth/66 protocol, not earlier one
- Log just a trace, not an error
2021-07-05 15:59:22 +02:00
sunce86 fdaee51ca0 Bump to v3.3.0-rc.3 2021-06-29 22:56:40 +02:00
Dusan Stanivukovic eec38b30e3
forcing base fee to zero if gas pricing is omitted (#460)
* forcing base fee to zero if gas pricing is omitted

* force base fee to zero for estimate_gas
2021-06-29 22:17:00 +02:00
Dusan Stanivukovic 287409f9f5
gasPrice substituted with effective_gas_price (#458) 2021-06-28 19:15:37 +02:00
Dusan Stanivukovic e5ae846de4
added effectiveGasPrice to eth_getTransactionReceipt return structure (#450) 2021-06-28 19:04:52 +02:00
Dusan Stanivukovic e6f3794dd4
rust version reverted to 1.52.1 (#454) 2021-06-28 16:39:18 +02:00
sunce86 5920f232d0 Bump to v3.3.0-rc.2 2021-06-17 11:32:11 +02:00
sunce86 3bce814090 Update changelog 2021-06-17 11:31:33 +02:00
sunce86 118051696e Merge branch 'dev' 2021-06-16 21:18:02 +02:00
Dusan Stanivukovic 17057eeedc
bump ethereum tests 9.0.2, revert london mainnet block (#435)
* bump ethereum tests 9.0.2
* london mainnet block removed
2021-06-16 20:48:23 +02:00
Dusan Stanivukovic 7aa1e987de
Update CHANGELOG.md 2021-06-16 09:33:16 +02:00
Jochen Müller 193b25a22d Bump to v3.3.0-rc.1 2021-06-15 13:25:26 +02:00
Jochen Müller 99a8ddae41 Update changelog 2021-06-15 13:00:45 +02:00
Dusan Stanivukovic 93b39df02d
Eip 3554 enabled, London blocks defined (#433)
* Eip 3554 enabled
* london hard fork blocks
2021-06-15 11:18:56 +02:00
Dusan Stanivukovic 5dec58ba9f
Sunce86/fix tx pool locals for 1559 (#431)
* fixed handling of local txs after 1559 activation
2021-06-11 10:19:08 +02:00
Dusan Stanivukovic b928380b64
test update (#429)
support for 9.0.1 ethereum tests
2021-06-10 15:01:48 +02:00
Dusan Stanivukovic 5e7086d54c
Sunce86/eip 1559 (#393)
* eip1559 hard fork activation

* eip1559 hard fork activation 2

* added new transaction type for eip1559

* added base fee field to block header

* fmt fix

* added base fee calculation. added block header validation against base fee

* fmt

* temporarily added modified transaction pool

* tx pool fix of PendingIterator

* tx pool fix of UnorderedIterator

* tx pool added test for set_scoring

* transaction pool changes

* added tests for eip1559 transaction and eip1559 receipt

* added test for eip1559 transaction execution

* block gas limit / block gas target handling

* base fee verification moved out of engine

* calculate_base_fee moved to EthereumMachine

* handling of base_fee_per_gas as part of seal

* handling of base_fee_per_gas changed. Different encoding/decoding of block header

* eip1559 transaction execution - gas price handling

* eip1559 transaction execution - verification, fee burning

* effectiveGasPrice removed from the receipt payload (specs)

* added support for 1559 txs in tx pool verification

* added Aleut test network configuration

* effective_tip_scaled replaced by typed_gas_price

* eip 3198 - Basefee opcode

* rpc - updated structs Block and Header

* rpc changes for 1559

* variable renaming according to spec

* - typed_gas_price renamed to effective_gas_price
- elasticity_multiplier definition moved to update_schedule()

* calculate_base_fee simplified

* Evm environment context temporary fix for gas limit

* fmt fix

* fixed fake_sign::sign_call

* temporary fix for GASLIMIT opcode to provide gas_target actually

* gas_target removed from block header according to spec change: https://github.com/ethereum/EIPs/pull/3566

* tx pool verification fix

* env_info base fee changed to Option

* fmt fix

* pretty format

* updated ethereum tests

* cache_pending refresh on each update of score

* code review fixes

* fmt fix

* code review fix - changed handling of eip1559_base_fee_max_change_denominator

* code review fix - modification.gas_price

* Skip gas_limit_bump for Aura

* gas_limit calculation changed to target ceil

* gas_limit calculation will target ceil on 1559 activation block

* transaction verification updated according spec: https://github.com/ethereum/EIPs/pull/3594

* updated json tests

* ethereum json tests fix for base_fee
2021-06-04 12:12:24 +02:00
Jochen Müller 144b2293a2
EIP 3541 (#422)
* Add EIP-3541 transition block parameter

* Implement EIP-3541

* Add a unit test for EIP-3541

* Add error type for attempt to deploy invalid code

* fmt
2021-05-31 14:37:23 +02:00
Jochen Müller f14d3e5a5c
EIP-3529: Reduction in gas refunds (#406)
* Implement changed gas refund rules according to EIP-3529

* Add a unit test for changed gas refunds

* Add missing comment

* Add fork block to spec.hard_forks
2021-05-19 17:18:01 +02:00
Jochen Müller f9f492638c Bump to v3.2.6 2021-05-14 09:39:01 +02:00
varasev fe198ddf7d
Berlin HF on POA Core and Sokol (#398)
Co-authored-by: POA <33550681+poa@users.noreply.github.com>
2021-05-11 11:10:51 +02:00
Jochen Müller 32d8b5487a Bump to v3.2.5 2021-05-05 15:14:10 +02:00
rakita 6835ec53ad
Remove windows build artifacts (#385)
Remove windows build artifacts that were added by mistake
2021-05-05 13:33:48 +02:00
Jochen Müller 09ab40b956
Mention v3.2.2 changes in v3.2.5 changelog (#386)
Since the two hotfix releases, v3.2.3 and v3.2.4, did not include the
changes from v3.2.2-rc.1, they should be mentioned in the changelog for
v3.2.5-rc.1 again.
2021-05-05 12:19:45 +02:00
Jochen Müller 2df74c26dd Bump to v3.2.5-rc.1 2021-04-28 15:08:33 +02:00
varasev ed12fffeef
Berlin HF on xDai (#384)
* Berlin HF on xDai

* Update enodes for xDai

Co-authored-by: rakita <rakita@users.noreply.github.com>
2021-04-28 14:07:24 +02:00
Jochen Müller c03cc15468
Implement eth/65 (#352)
* EIP-2464: eth/65: transaction announcements and retrievals

* Updates to eth-65 implementatiom

- Replace deprecated syntax and method name
- Replace call to removed method
- Reimplement rlp::Encodable for SignedTransaction

* fmt

* More updates

- Replace calls to removed methods
- More dyns

* Apply requested changes

- Avoid unused variable warnings
- Avoid implementing Encodable trait for Transaction
- Update message encoding to EIP-2718 transaction format

* Update sync stats of eth-65 peers properly

* fmt & fix error propagation

* Fix: Add correct subset of transactions to stats.

* Update list of last sent transactions correctly.

Co-authored-by: Artem Vorotnikov <artem@vorotnikov.me>
Co-authored-by: Karim Agha <karim.dev@gmail.com>
Co-authored-by: rakita <rakita@users.noreply.github.com>
2021-04-27 15:33:40 +02:00
rakita 3b3ecf6676
Bump eth tests to 8.0.3 (#349) 2021-04-27 14:36:50 +02:00
rakita be9b16ab67
Bump parking_lot to 0.11.1. Revert std mutex from #277 (#350) 2021-04-27 13:12:19 +02:00
varasev 392a909cb7
Berlin HF in Kovan (#380)
* Berlin HF in Kovan

According to https://github.com/poanetwork/poa-chain-spec/pull/171

* Update nodes for Kovan
2021-04-26 10:21:01 +02:00
Jochen Müller 6f5a00a642 Merge branch 'main' into dev 2021-04-21 15:42:43 +02:00
Jochen Müller 2453f1a803 Merge branch 'release/v3.2.x' into main 2021-04-21 15:27:39 +02:00
rakita 09967329af Bump to v3.2.4 2021-04-15 21:49:20 +02:00
rakita 459a1a02a4 Fix broadcast for typed tx 2021-04-15 21:47:50 +02:00
Wei Tang a716eb3871 EIP-2929: only add params.address to access list 2021-04-15 18:15:40 +02:00
Wei Tang 0fd7c59724 EIP-2929: add tx sender and address into the access list 2021-04-15 18:02:24 +02:00
rakita aa41520dd1 Bump to v3.2.3 2021-04-15 18:02:24 +02:00
Wei Tang 4bffab6715 Fix compile 2021-04-15 18:02:24 +02:00
Wei Tang 5709dbc3e0 EIP2929: only add builtin to warm address if they are active 2021-04-15 18:02:24 +02:00
Wei Tang 6ce6666cbb
EIP-2929: add tx sender and address into the access list (#360) 2021-04-15 17:41:33 +02:00
rakita 582bca385f Bump to v3.2.2-rc.1. Changelog 2021-03-31 15:07:27 +02:00
rakita 9037ad0ea7 Bump eth/tests to v8.0.2 2021-03-31 13:44:14 +02:00
rakita c9190a39ed
Executable queue for ancient blocks inclusion (#208)
* Executable queue for ancient blocks inclusion
* Add drop trait for client
* Added shutdown to tests
* Remove doubled call
* Use reth-util from reth repo
2021-03-25 16:04:32 +01:00
rakita 85391f99ac
Backport AuRa commits for xdai (#330)
* Add SealingState; don't prepare block when not ready. (#10529)
* Fix a few typos and unused warnings. #10803
* Configuration map of block reward contract addresses (#10875)
* Step duration map configuration parameter ported from the POA Network fork (#10902)
* Add a 2/3 quorum option to Authority Round. (#10909)
* Additional arithmetic EVM opcode benchmarks (#10916)
* RPC method for clearing the engine signer (#10920)
* authority_round: Fix next_step_time_duration. (#11379)
* Aura: Report malice on sibling blocks from the same validator (#11160)
* TxPermissions ver 3: gas price & data (#11170)
* Add randomness contract support to AuthorityRound. (#10946)
* Set the block gas limit to the value returned by a contract call (#10928)
* AuthorityEngine: Minor cleanups. (#11408)
* Add POSDAO transition and malice report queue. (#11245)
* PoA call validators on_close_block
* Actualize spec files for POA Networks
* Some fixes after merge
* Crypto error desc
* AuRa on_close_block Error::Old fix

Co-authored-by: POA <33550681+poa@users.noreply.github.com>
2021-03-25 14:37:01 +01:00
rakita 561ed8df3c fix miner_author by adding dummy msg 2021-03-24 17:50:36 +01:00
rakita 5eacff59e8 Revert "Remove eth/63 protocol version (#252)"
This reverts commit bbecb0415e.
2021-03-23 11:59:33 +01:00
rakita d030870220 Merge remote-tracking branch 'origin/main' into dev 2021-03-21 21:58:13 +01:00
Karim Agha ee88247e71 Fixing outdated readme links (#322) 2021-03-21 21:58:02 +01:00
Jochen Müller 37f5291538
Add Nethermind to clients that accept service transactions (#324)
* Add Nethermind to clients that accept service transactions

* fmt
2021-03-18 14:00:05 +01:00
rakita 5fdedf0858 Bump to v3.2.1. Changelog 2021-03-17 09:46:36 +01:00
Jochen Müller 504777e879
Implement the filter argument in parity_pendingTransactions (#295)
* Add filters for pending transactions to RPC API

Allow filtering results in the parity_pendingTransaction endpoint as described in the API docs.

* Make arguments in parity_pendingTransactions work together

filter and limit

* fmt

* Requested changes

- filter in ethcore to avoid unneccessary copying
- rename gas_price to gasPrice
- implement requesting contract creation txs with "action"

* Some beautifying

Remove missing import and unneccessary dependency entry, add a comment
and set right lint level on new module

* fixed broken build after merge

* fmt

* fixing CI errors: type conversion

Co-authored-by: Karim Agha <karim.dev@gmail.com>
2021-03-16 13:39:42 +01:00
rakita 3317797285 Initial sync block stuck. Block import logs (#318) 2021-03-16 12:08:22 +01:00
rakita 33a3a9deec
Initial sync block stuck. Block import logs (#318)
Co-authored-by: Karim Agha <karim.dev@gmail.com>
2021-03-14 11:18:38 +01:00
Karim Agha 327c4bcb14
Fixing outdated readme links (#322) 2021-03-14 10:25:45 +01:00
Karim Agha b6b5129058
Following up on PR #316, removing unused conversions that are now deadcode (#321) 2021-03-14 10:25:14 +01:00
Karim Agha 29dc10c446
Updating ethcore and ethjson crates to Rust Edition 2018 (#316)
* Migrating use crate::Type imports to Rust 2018 edition

* import name fixes

* fmt

* removing diff leftover

* catching up with latest developments on dev

* fmt

* removing another diff leftover
2021-03-12 14:55:49 +01:00
Simon KP 48e7d6cee4
Send SIGTERM instead of SIGHUP to OE daemon (#317)
* Send SIGTERM instead of SIGHUP to OE daemon

* Address comments
2021-03-12 13:00:09 +01:00
rakita a0f406e26b
Ethereum-types and various libs upgrade (#315)
* Upgrade Eth types

ethereum-types -> 0.9.2
rlp -> 0.4.6
keccak-hash -> 0.5.0
parity-crypto -> 0.6.2
ethabi -> 0.12.0
ethabi-derive -> 0.12.0
ethabi-contract -> 0.11.0
ethbloom -> 0.9.1
rand -> 0.7.3
trie-standardmap -> 0.15.2
triehash -> 0.5.0

* backport #10714. Small changes, merge fixes

Co-authored-by: mdben1247 <mdben1247@users.noreply.github.com>
2021-03-12 10:12:42 +01:00
rakita 3f8e0cfec4 Merge remote-tracking branch 'origin/main' into dev 2021-03-11 16:30:47 +01:00
rakita f143ddb75a [devops] Upgrade docker alpine to v1.13.2 2021-03-11 08:42:23 +01:00
rakita 32fd2b484c Merge remote-tracking branch 'origin/main' into dev 2021-03-10 17:23:00 +01:00
rakita 187c81b3f1 Upgrade ethereum/tests to v8.0.1 (#301) 2021-03-10 17:18:17 +01:00
rakita d7a958129f
[evmbin] Omit storage output, now for std-json (#311)
* [evmbin] Omit storage output, now for std-json
* fix tests
2021-03-10 16:39:32 +01:00
rakita eca8fb74ae
Strict memory order (#306)
* Make MemoryOrdering more strict

* fmt

* Strict mem order for priority_tasks_gate
2021-03-10 12:36:23 +01:00
Karim Agha e2024c4b81
Fixing a compilation warning (#308)
* Fixing a compilation warning
2021-03-10 09:46:34 +01:00
rakita 5b904476cd
Backport: Block sync stopped without any errors. #277 (#286)
* Backport: Block sync stopped without any errors. #277
* fmt
2021-03-09 11:20:32 +01:00
rakita 7ea5707904
Freeze pruning while creating snapshot (#205)
* Freeze pruning while creating snapshot
* Use scopeguard for snapshot generation
* Snapshot 1k blocks
* Snapshot number correction
2021-03-09 09:47:49 +01:00
rakita 458d55559e Bump to v3.2.0 2021-03-08 17:49:59 +01:00
rakita 6f50061f0c
AuRa multi block reward (#290)
* Multi block reward for AuRa
* Added test
* Better error on wrong config
2021-03-08 11:59:04 +01:00
gakada dbc5f94241
Migrate compare_and_swap to compare_exchange (#291) 2021-03-06 11:30:52 +01:00
rakita 0cf0cdbb86 Disable CI on tag for windows2019 machine"
This reverts commit efb80e1032.
2021-03-05 19:27:01 +01:00
rakita 91e57c803d Disable EIP-2315 2021-03-05 18:01:27 +01:00
adria0.eth 0fcb102f03
Improved metrics (#240)
Added db metrics (kvdb_bytes_read, kvdb_bytes_written, kvdb_reads, kvdb_writes)
Added --metrics-prefix=[prefix]
2021-03-03 22:44:35 +01:00
Dusan Stanivukovic 973a5a594b
Sunce86/add support eip2930 ethereum tests runner (#288)
* added support for eip2930 tests

Co-authored-by: sunce86 <dusan.stanivukovic@gmail.pm>
2021-03-03 15:57:25 +01:00
rakita ba011eba15
Send RLPx auth in EIP-8 format (#287) 2021-03-03 12:58:10 +01:00
Anonymous 7c9eed8d65 ethcore/snapshot: fix double-lock in Service::feed_chunk (#289) 2021-03-02 14:44:58 +01:00
Anonymous 63fdad8d86
ethcore/snapshot: fix double-lock in Service::feed_chunk (#289) 2021-03-02 14:27:28 +01:00
Dusan Stanivukovic 0947261cf2
Sunce86/rpc module reverted for RPC JSON api (#284)
* rpc module reverted for RPC JSON api

Co-authored-by: Dusan Stanivukovic <dusan.stanivukovic@gnosis.pm>
2021-02-26 15:23:29 +01:00
Giacomo efb80e1032
Enable CI on windows2019 machine (#283)
Enable CI on windows2019 machine

* Add -y for choco install
2021-02-26 10:35:17 +01:00
draganrakita f0fd88aa12 Github action path for folder structure 2021-02-25 17:21:02 +01:00
draganrakita 142b63a4f9 Backport github actions from old main 2021-02-25 17:05:24 +01:00
rakita 1d2b640834 chocolate -y confirmation steps 2021-02-25 11:11:31 +01:00
draganrakita f1dc682168 Bump to 3.2.0-rc.1 2021-02-24 13:00:50 +01:00
draganrakita 0bb2f8f6b8 Berlin hardfork blocks 2021-02-24 13:00:50 +01:00
rakita d5c2a0fbe2
Fix for TypedTx enabling and hash generation. Tweak sig V in RPC. (#272)
* Fix for TypedTx hash generation from RlpView
* Tweaks on sig V field in RPC
* Fix for eip2930 incomming tx
2021-02-23 13:46:08 +01:00
rakita 98563b0a45
Change ProtocolId to U64. yolo3x spec (#271)
* Change ProtocolId to U64 and make it support variable subprotocol names* 
* Add yolo3x testnet
2021-02-19 12:52:24 +01:00
adria0.eth d8ce175846
Fix modexp, update tests, update berlin chainspec (#267) 2021-02-17 19:43:09 +01:00
rakita fb9699d8e1
EIP-2930 remove type from legacy JSONRPC (#265)
* EIP-2930 remove type from legacy JSONRPC
* fmt
2021-02-17 16:52:51 +01:00
rakita dbf9a1cd98
EIP-2930 spec change for signature hash (#261)
* EIP-2930 spec change for signature hash
* Removing eip2930 test blocks
2021-02-10 15:58:35 +01:00
rakita 6b4e56b214
AccessList in JSONRPC. And enabling github action tests (#255)
* Enabling github action tests
* Fix failing tests
* AccessList to Option in json
* failing rust example removed
* AccessList for jsonrpc
* Tx type as sequence, AccessList as type
2021-02-08 14:55:03 +01:00
Jochen Müller f40e198eb7
Give IoService a consistent interface (#257)
The start() method had two different signatures depending on configuration. Since ethcore is built with either config depending on the build target, this resulted in compiler errors for either evmbin or oe.
2021-02-05 13:33:39 +01:00
rakita bbecb0415e
Remove eth/63 protocol version (#252)
* Remove eth/63 protocol
2021-02-04 14:10:48 +01:00
rakita 6d81fce451
Update EWF's chains with Istanbul transition block numbers (#11482) (#254)
Co-authored-by: Artem Vorotnikov <artem@vorotnikov.me>
2021-02-04 11:39:10 +01:00
rakita 65c5e6dfd3
EIP-2930 RPC and TypedTransactionView support (#245)
* TypedTransactionView for EIP-2930
* Enable EIP-2930 in RPC calls
* type field added to TypedTransactionView
2021-02-02 16:26:55 +01:00
adria0.eth 2e23ca353f
Use berlin chainspec for unit test, yolo3 for testnet (#244)
* Fix yolo+berlin specs
* fix yolo3 maximumExtraDataSize
* fmt
2021-02-01 16:40:00 +01:00
rakita a831379ad8
Yolo3 spec (#241)
Update Yolo3
2021-02-01 08:34:44 +01:00
rakita cfc6439f2e
Local EIP2930 and EN/DE block tests (#237) 2021-01-28 17:23:01 +01:00
François Garillot 52d966ccaa
Cleans up a number of Option / Result patterns and warts (#226) 2021-01-21 18:27:35 +01:00
rakita 59d891edf4
Snapshot manifest block added to prometheus (#232)
Co-authored-by: adria0.eth <5526331+adria0@users.noreply.github.com>
2021-01-21 17:23:15 +01:00
draganrakita f3bdc0da3c Bump to 3.1.1 2021-01-19 14:49:25 +01:00
Denis Granha a55799d523
Set alpine version to 3.12.3
Alpine version is changed in order to prevent Cmake errors. Reference: https://gitlab.alpinelinux.org/alpine/aports/-/issues/12321.

Intends to solve https://github.com/openethereum/openethereum/issues/230
2021-01-19 10:59:40 +01:00
draganrakita 1d07c4c06b fix Supplied instant is later than self 2021-01-18 09:34:48 +01:00
rakita ea25ffd79d
Added additional Sg-1,Ca-2,Ca-3 OE bootnodes (#222) 2021-01-17 23:15:33 +01:00
Adria Massanet eb876cb2d7 CI fix 2021-01-14 15:30:57 +01:00
Adria Massanet 814526a248 Fix CI 2021-01-14 15:30:57 +01:00
Adria Massanet d3ba83405c fmt 2021-01-14 15:30:57 +01:00
Adria Massanet c46fe330dc Big folder refactor 2021-01-14 15:30:57 +01:00
draganrakita 0e5d6944b7 Add eip2929,eip2930 to ForkId list 2021-01-11 15:21:16 +01:00
adria0.eth b0a1e3da03
Update ethereum/tests to f55f344 (#220) 2021-01-11 14:14:41 +01:00
rakita 0706e5468d
EIP-1898: Allow default block parameter to be blockHash (#203)
* Allow default block parameter to be blockHash

Backport to 3.1 of https://github.com/openethereum/openethereum/pull/10932

Co-authored-by: Richard Patel <me@terorie.dev>

* Request canonical with BlockHash

Co-authored-by: Seun LanLege <seunlanlege@gmail.com>
Co-authored-by: Richard Patel <me@terorie.dev>
2021-01-11 11:00:27 +01:00
rakita f286597d10
Update link to issue (#202) 2021-01-08 15:56:53 +01:00
Giacomo e2f665e9cf
Enable Windows2019 CI on main/dev (#213)
* Revert "Disable windows2019, remove tagged windows artifact"

This reverts commit 08e6cca3e5.

* Allow running CI on windows only when pushing to main/dev

* Remove unneeded block code
2020-12-30 15:07:20 +01:00
draganrakita eab41b49cf test cfg 2020-12-23 15:31:13 +01:00
draganrakita 8d3e0582a8 fmt 2020-12-23 15:31:13 +01:00
draganrakita 705bc71593 Payload limit for test 2020-12-23 15:31:13 +01:00
draganrakita f723e288c3 Cleanup devp2p unused interface fn 2020-12-23 15:31:13 +01:00
Justin Beaurone a6bd3516e0 OpenEthereum rebranding 2020-12-21 13:34:05 +01:00
draganrakita 08e6cca3e5 Disable windows2019, remove tagged windows artifact 2020-12-21 11:54:53 +01:00
draganrakita 8a9d14141a Revert "Fix CI problems (#127)" and Remove sscache
This reverts commit 12afb13e9b.
2020-12-21 11:45:52 +01:00
draganrakita 832fc444b6 Tweaks in informer log 2020-12-15 17:07:49 +01:00
adria0.eth 612a71ecb2 Update ISSUE_TEMPLATE.md (#124)
Tell users to ask questions in discord instead of opening an issue.
2020-12-15 11:39:34 +01:00
Giacomo 06fc61d7c5
Add custom windows runner (#162) 2020-12-11 11:18:18 +01:00
rakita 837e8b8725
Ancient target set. InvalidStateRoot bug (#69) (#149) 2020-12-10 17:57:26 +01:00
rakita ea3efd926e
TypedTransaction (EIP-2718) and Optional access list (EIP-2930) (#135) 2020-12-10 16:42:05 +01:00
rakita 3f01b69084
Fix broken doc comments (#151) 2020-12-09 12:43:32 +01:00
rakita 647ff31942
Add ws-max-paxload (#155) 2020-12-09 11:48:27 +01:00
rakita 56131b6d92
Trace comment on new block inclusion (#100) 2020-12-02 11:31:11 +01:00
rakita 51d824fbdc
Remove sscache (#138) 2020-12-01 16:40:06 +01:00
rakita 1225ff2c5a
Add flag to disable storage output in openethereum-evm tool #97 (#115) 2020-11-26 08:31:44 +01:00
adria0.eth cb91b7e828
Fix CI problems (#127)
* Temporally Fix CI compilation
2020-11-25 18:28:17 +01:00
Adria Massanet 233bab2ee7 Update linked-hash-map to 0.5.3 2020-11-24 18:11:29 +00:00
Lachezar Lechev fed80cc075
ethstore - remove unnecessary dir & tiny-keccak dependencies from the lib (#107)
* ethstore - remove unnecessary dir & tiny-keccak dependencies
2020-11-23 13:49:32 +01:00
Giacomo 26ab00b6c7
Fix deprecated set-env declaration (#106)
* Fix deprecated set-env declaration

* Fix add-path and set-env in install-sscache.ps1
2020-11-17 11:16:24 +01:00
Giacomo 01e72efb80 Feature/improve dockerhub deployment (#98)
* Add docker deployment for Github actions

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

Co-authored-by: Denis Granha <denis@gnosis.pm>
2020-11-16 18:30:13 +01:00
adria0.eth 81ae80b7f1
EIP2565 impl (#82)
EIP2565 implementation
2020-11-12 14:31:24 +01:00
adria0.eth 910bb78f0d
Downgrade sccache to 1.1.2 (#93) 2020-11-12 12:57:35 +01:00
adria0.eth 6078eeaed7
EIP2929 with journaling + Yolov3 (#79) 2020-11-04 19:11:05 +01:00
rakita 50a4d5fa57
Sync block verification (#74)
* Synchronize block verification
* max_round_blocks_to_import set to 1
* Fixed test that rely on 12block batches

Co-authored-by: adria0.eth <5526331+adria0@users.noreply.github.com>
2020-10-27 10:45:48 +01:00
Adria Massanet 410853e280 Update gitactions for dev branch, deprecate stable branch 2020-10-26 09:27:44 +00:00
Denis Granha bf5830f766
revert actions/cache to version 1.1.2 (#80) 2020-10-23 22:59:18 +02:00
rakita d811f6e3ce
Use ubuntu-16.04 for glibc compatibility (#11888) (#73) 2020-10-23 10:43:18 +02:00
adria0.eth 9110b1d9e4
Update gitactions master->main (#72) 2020-10-12 10:55:21 +02:00
varasev cb0513a8b1
Add `wasmDisableTransition` spec option (#60)
* Add wasmDisableTransition spec option
2020-10-08 22:37:48 +02:00
adria0.eth 84f675021c
Fix warnings (#64) 2020-09-30 13:10:54 +02:00
rakita 03bfb012a1
Change wiki links (#68) 2020-09-30 13:10:18 +02:00
Denis Granha 16542bd355
fix CD env param 2020-09-29 13:04:59 +02:00
Giacomo 24cff45334
Set AWS_REGION as a global env variable (#67) 2020-09-28 17:07:36 +02:00
adria0.eth 51817baecd
Bump to 3.1rc1 (#58) 2020-09-25 10:49:22 +02:00
rakita 15ebc98877
Drain the transaction overlay (#11654) (#59) 2020-09-25 10:46:51 +02:00
claveros b6a25ba30a
Update CHANGELOG to 3.1 (#56)
* Update CHANGELOG.md

Backport 3.1 Changelog

* Update CHANGELOG.md

3.0.0, 3.0.1, 3.1 changelog

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Added 3.0.x changelog

Co-authored-by: draganrakita <dragan0rakita@gmail.com>
2020-09-24 17:36:53 +03:00
rakita abceaf3832
fix migration check when version_db file does no exist (#57) 2020-09-23 19:57:52 +02:00
rakita 25c2f7e7fd
Dont upgrade for old versions of DB (#54) 2020-09-23 13:49:25 +02:00
rakita 27a0142af1
OpenEthereum rebranding (#31) 2020-09-22 14:53:52 +02:00
adria0.eth 698fa6e8f6
Remove classic, kotti, mordor, expanse (#52) 2020-09-22 13:45:38 +02:00
rakita ae312bcb01
Added bad block header hash for ropsten (#49)
* Added bad block header hash for ropsten
2020-09-22 13:29:03 +02:00
Artem Vorotnikov d17ee979b8
Remove accounts bloom (#33) 2020-09-22 12:41:04 +02:00
draganrakita 4fb4ef6d24 Bump jsonrpc-* to v15 2020-09-21 16:11:29 +00:00
adria0.eth 1c82a0733f
Implement eth/64, remove eth/62 (#46)
Co-authored-by: Artem Vorotnikov <artem@vorotnikov.me>
2020-09-21 14:48:14 +02:00
adria0.eth b54ddd027d
No snapshotting by default (#11814) 2020-09-15 17:51:49 +03:00
Artem Vorotnikov 61e56aba41
Update Ellaism chainspec 2020-09-15 17:34:51 +03:00
rakita aecc6fc862
Prometheus, heavy memory calls removed (#27) 2020-09-14 16:08:57 +02:00
adria0.eth dd38573a28
Update ethereum/tests 2020-09-10 09:04:14 +03:00
adria0.eth c84d82580a
Implement JSON test suite (#11801) 2020-09-10 05:54:38 +03:00
rakita 506cee52e8
Fix issues during block sync (#11265) 2020-09-10 05:54:37 +03:00
rakita c58b52c21c
verification: fix race same block (#11400) 2020-09-10 05:54:37 +03:00
adria0.eth f8326b6e27
EIP-2537: Precompile for BLS12-381 curve operations (#11707) 2020-09-10 05:54:37 +03:00
Artem Vorotnikov ea0c13c0a4
Remove private transactions 2020-09-10 05:54:37 +03:00
rakita a8668b371c
Remove GetNodeData 2020-09-10 05:54:36 +03:00
Artem Vorotnikov 32ea4d69a3
Remove IPFS integration (#11532) 2020-09-10 05:54:36 +03:00
Artem Vorotnikov defd24c40e
Remove updater 2020-09-10 05:54:36 +03:00
Artem Vorotnikov 194101ed00
Remove light client 2020-09-10 05:54:36 +03:00
Artem Vorotnikov 2ab8c72ce3
Correct internal par protocol nomenclature 2020-09-10 05:54:30 +03:00
Artem Vorotnikov a75ba3620c
Remove C and Java bindings (#11346) 2020-09-10 05:54:10 +03:00
Artem Vorotnikov 54afb33333
Remove whisper (#10855) 2020-09-10 05:53:41 +03:00
Artem Vorotnikov 3f42b6178f
Remove security audit workflow for now 2020-09-10 05:53:41 +03:00
adria0.eth 1460f6cc27
EIP-2315: Simple Subroutines for the EVM (#11629) 2020-09-10 05:53:41 +03:00
Artem Vorotnikov 751210c963
Remove deprecated flags 2020-09-10 05:53:41 +03:00
Artem Vorotnikov 7dfa57999b
Remove failing test 2020-09-10 05:53:41 +03:00
Artem Vorotnikov 11fb967c6a
Remove support for hardware wallets (#10678) 2020-09-10 05:53:29 +03:00
Artem Vorotnikov c270599a23
Fix warnings: other 2020-08-07 20:47:14 +03:00
adria0 412d0307cb
Fix warnings: try/? in whisper 2020-08-07 20:47:14 +03:00
adria0 33b5b36f44
Fix warnings: single tuples 2020-08-07 20:47:14 +03:00
adria0 ef7a82835a
Fix warnings: future name collisions 2020-08-07 20:47:14 +03:00
adria0 ac8f65dbbf
Fix warnings: iter 2020-08-07 20:47:13 +03:00
adria0 725073a683
Fix warnings: docs 2020-08-07 20:47:13 +03:00
adria0 c5aed5bab1
Fix warnings: unnecessary mut 2020-08-07 20:47:13 +03:00
adria0 cacbf256fe
Fix warnings: unused 2020-08-07 20:47:13 +03:00
adria0 0cd972326c
Fix warnings: mem::uninitialized 2020-08-07 20:47:13 +03:00
adria0 239cf91594
Fix warnings: range patterns 2020-08-07 20:47:13 +03:00
adria0 1700873f48
Fix warnings: dyn 2020-08-07 20:47:12 +03:00
adria0 4adb44155d
Update bootnodes 2020-08-07 20:47:12 +03:00
adria0 82b37bfa0d
Add github actions 2020-08-07 20:47:12 +03:00
adria0 544725a018
Fix compilation errors 2020-08-07 20:47:12 +03:00
Artem Vorotnikov 26e253e554
Ignore reformat commit in git blame 2020-08-07 20:47:09 +03:00
Artem Vorotnikov 610d9baba4
Reformat the source code 2020-08-07 20:46:52 +03:00
s3krit 253ff3f37b
Update version and changelog (#11360) 2019-12-31 00:47:57 +01:00
Tomasz Drwięga 54c2d6167f Make sure to not mark block header hash as invalid if only the body is wrong. (#11356)
* Patch invalid transaction root.

* Add raw hash to bad and include fix for uncles too.

* Fix submodules.
2019-12-30 13:15:11 -08:00
David 29ebddc64f V2.5.12 stable (#11336)
* Enable EIP-2384 for ice age hard fork (#11281)
* ethcore/res: activate agharta on classic 9573000 (#11331)
* Istanbul HF in xDai (2019-12-12) (#11299)
* Istanbul HF in POA Core (2019-12-19) (#11298)
* Istanbul HF in POA Sokol (2019-12-05) (#11282)
* Activate ecip-1061 on kotti and mordor (#11338)
* Enable basic verification of local transactions (#11332)
* Disallow EIP-86 style null signatures for transactions outside tests (#11335)
2019-12-16 14:53:34 +01:00
Niklas Adolfsson fc129b4a26 [stable backport]: add `eip1344_transition` for istanbul (#11301) (#11303)
* [chainspec]: add `eip1344_transition` for istanbul (#11301)

* Update version and CHANGELOG.md
2019-12-05 14:11:55 +01:00
s3krit a071d81fe9
update CHANGELOG.md (#11263) 2019-11-14 17:42:49 +01:00
Denis S. Soldatov aka General-Beck 9f94473eaf `sccache --stop-server -> sccache --show-stats` (#11255) 2019-11-12 18:23:08 +01:00
s3krit 42437fbc57
fix windows libusb builds (#11251)
* fix libusb on Windows
2019-11-12 16:52:34 +01:00
Talha Cross f3cdd7bf21 v2.5.10 stable (#11239)
* ropsten #6631425 foundation #8798209 (#11201)
* [stable] builtin, istanbul and mordor testnet backports (#11234)
  * ethcore-builtin (#10850)
  * [builtin]: support `multiple prices and activations` in chain spec (#11039)
  * [chain specs]: activate `Istanbul` on mainnet (#11228)
  * ethcore/res: add mordor testnet configuration (#11200)
* Update list of bootnodes for xDai chain (#11236)
* ethcore: remove `test-helper feat` from build (#11047)
* Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159)
* [stable]: backport #10691 and #10683 (#11143)
  * Fix compiler warning (that will become an error) (#10683)
  * Refactor Clique stepping (#10691)
* Add Constantinople eips to the dev (instant_seal) config (#10809)
* Add cargo-remote dir to .gitignore (?)
* Insert explicit warning into the panic hook (#11225)
* Fix docker centos build (#11226)
* Update MIX bootnodes. (#11203)
* Use provided usd-per-eth value if an endpoint is specified (#11209)
* Add new line after writing block to hex file. (#10984)
* Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…)
* Upgrade jsonrpc to latest (#11206)
* [CI] check evmbin build (#11096)
* Correct EIP-712 encoding (#11092)
* [client]: Fix for incorrectly dropped consensus messages (#11086)
* Fix block detail updating (#11015)
* Switching sccache from local to Redis (#10971)
* Made ecrecover implementation trait public (#11188)
* [dependencies]: jsonrpc `14.0.1` (#11183)
* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179)
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
* Cleanup stratum a bit (#11161)
* Upgrade to jsonrpc v14 (#11151)
* SecretStore: expose restore_key_public in HTTP API (#10241)
2019-11-11 21:57:38 +01:00
Niklas Adolfsson 5ee54b7298 [stable]: backports (#11114)
* [spec] fix rinkeby spec (#11108)

* use images from our registry (#11105)
2019-10-02 12:23:20 +02:00
s3krit 06c7096054
Update CHANGELOG.md and version (#11093)
* Update CHANGELOG.md and version
2019-09-26 12:38:49 +02:00
s3krit b2277f65e4
v2.5.9-stable (#11089)
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)

* ethcore/res: activate Istanbul on Ropsten block 6485846

* ethcore/res: activate Istanbul on Goerli block 1561651

* ethcore/res: use hex values for Istanbul specs

* ethcore/res: fix trailing comma

* ethcore/res: be pedantic about EIP-1283 in Petersburg and Istanbul test specs

* ethcore/res: activate Istanbul on Rinkeby block 5435345

* ethcore/res: activate Istanbul on Kovan block 14111141

* ethcore/res: fix kovan istanbul number to 0xd751a5

* [json-spec] make blake2 pricing spec more readable (#11034)

* [json-spec] make blake2 pricing spec more readable

* [ethcore] fix compilation

* Manual backport of #11033
2019-09-26 11:03:39 +02:00
s3krit c52a6c8fb7
update CHANGELOG.md (#11057) 2019-09-16 12:00:04 +02:00
s3krit 7c7b181ca0
v2.5.8-stable (rev2) (#11051)
* EIP 1884 Re-pricing of trie-size dependent operations  (#10992)
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200  (#10191)
2019-09-13 15:46:03 +02:00
David 24a4fdf405
Don't build rpc with ethcore test-helpers (#11048) 2019-09-13 11:53:49 +02:00
s3krit 45f27cec34
v2.5.8-stable (#11041)
* add more tx tests (#11038)
* Fix parallel transactions race-condition (#10995)
* Add blake2_f precompile (#11017)
* [trace] introduce trace failed to Ext (#11019)
* Edit publish-onchain.sh to use https (#11016)
* Fix deadlock in network-devp2p (#11013)
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
* xDai chain support and nodes list update (#10989)
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
* EIP-1344 Add CHAINID op-code (#10983)
* manual publish jobs for releases, no changes for nightlies (#10977)
* [blooms-db] Fix benchmarks (#10974)
* Verify transaction against its block during import (#10954)
* Better error message for rpc gas price errors (#10931)
* tx-pool: accept local tx with higher gas price when pool full (#10901)
* Fix fork choice (#10837)
* Cleanup unused vm dependencies (#10787)
* Fix compilation on recent nightlies (#10991)
2019-09-12 18:43:53 +02:00
s3krit 6bd7db96fe
v2.5.7 stable (#11006)
* [trace] check mem diff within range (#11002)

* Update version (v2.5.7-stable)
2019-08-29 12:06:49 +02:00
s3krit ff398fe7ff
V2.5.6 stable (#10961)
-  Fix cargo audit (#10921)
  - 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)
  - Do not drop the peer with None difficulty (#10772)
  - docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
2019-08-12 18:55:11 +02:00
s3krit 3ebc769757
version: stabilise v2.5 (#10857) 2019-07-08 12:30:14 +02:00
s3krit d60e6384d7
Beta 2.5.4 (#10827)
* cargo update -p smallvec (#10822)

Fixes https://github.com/servo/rust-smallvec/issues/148

* Update version to v2.5.3

Signed-off-by: Martin Pugh <pugh@s3kr.it>
2019-07-01 12:33:35 +02:00
s3krit 3fd58bdcbd
Beta 2.5.3 (#10776)
* ethcore/res: activate atlantis classic hf on block 8772000 (#10766)

* fix docker tags for publishing (#10741)

* fix: aura don't add `SystemTime::now()` (#10720)

This commit does the following:
- Prevent overflow in `verify_timestamp()` by not adding `now` to found faulty timestamp
- Use explicit `CheckedSystemTime::checked_add` to prevent potential consensus issues because SystemTime is platform
depedent
- remove `#[cfg(not(time_checked_add))]` conditional compilation

* Update version

* Treat empty account the same as non-exist accounts in EIP-1052 (#10775)

* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet (#10705)

* get node IP address and udp port from Socket, if not included in PING packet

* prevent bootnodes from being added to host nodes

* code corrections

* code corrections

* code corrections

* code corrections

* docs

* code corrections

* code corrections

* Apply suggestions from code review

Co-Authored-By: David <dvdplm@gmail.com>

* Add a way to signal shutdown to snapshotting threads (#10744)

* Add a way to signal shutdown to snapshotting threads

* Pass Progress to fat_rlps() so we can abort from there too.

* Checking for abort in a single spot

* Remove nightly-only weak/strong counts

* fix warning

* Fix tests

* Add dummy impl to abort snapshots

* Add another dummy impl for TestSnapshotService

* Remove debugging code

* Return error instead of the odd Ok(())
Switch to AtomicU64

* revert .as_bytes() change

* fix build

* fix build maybe
2019-06-25 13:38:29 +00:00
Talha Cross ecbafb2390
backports for beta 2.5.2 (#10737)
* version: bump beta to 2.5.2

* [CI] allow cargo audit to fail (#10676)

* [CI] allow cargo audit to fail

* [.gitlab-ci.yml] add a comment about cargo audit

* [Cargo.lock] cargo update -p protobuf

* Reset blockchain properly (#10669)

* delete BlockDetails from COL_EXTRA

* better proofs

* added tests

* PR suggestions

* new image (#10673)

* Update publishing (#10644)

* docker images are now built on k8s: test run

* copy check_sync.sh in build-linux job

* copy scripts/docker/hub/* in build-linux job

* removed cache var

* cleanup, no more nightly dockers

* cleanup in dockerfile

* some new tags

* removed sccsche debug log, cleanup

* no_gits, new artifacts dir, changed scripts. Test run.

* define version once

* one source for TRACK

* stop kovan onchain updates

* moved changes for two images to a new branch

* rename Dockerfile

* no need in libudev-dev

* enable lto for release builds (#10717)

* Use RUSTFLAGS to set the optimization level (#10719)

* Use RUSTFLAGS to set the optimization level

Cargo has a [quirk]() in how configuration settings are propagated when `cargo test` runs: local code respect the settings in `[profile.test]` but all dependencies use the `[profile.dev]` settings. Here we force `opt-level=3` for all dependencies.

* Remove unused profile settings

* Maybe like this?

* Turn off incremental compilation

* Remove colors; try again with overflow-checks on

* Use quiet CI machine

* Turn overflow checking back on

* Be explicit about what options we use

* Remove "quiet machine" override

* ethcore: enable ECIP-1054 for classic (#10731)

* config: enable atlantis on ethereum classic

* config: enable atlantis on morden classic

* config: enable atlantis on morden classic

* config: enable atlantis on kotti classic

* ethcore: move kotti fork block to 0xAEF49

* ethcore: move morden fork block to 0x4829BA

* ethcore: move classic fork block to 0x81B320

* remove trailing comma

* remove trailing comma

* fix chainspec

* ethcore: move classic fork block to 0x7fffffffffffffff
2019-06-11 20:56:03 +02:00
Talha Cross adabd8198c
beta ci: backport missing diff from master (#10661)
* ci: publish docs debug (#10638)

* ci: backport missing diff from master
2019-05-14 15:03:29 +02:00
Talha Cross c2487cfe07
ci: publish docs debug (#10638) (#10660) 2019-05-14 14:04:54 +02:00
Talha Cross e0141f8324
beta 2.5.1 (#10643)
* version: bump beta to 2.5.1

* fix(whisper expiry): current time + work + ttl (#10587)

* update bootnodes (#10595)

* config: update goerli bootnodes

* config: update kotti bootnodes

* adds rpc error message for --no-ancient-blocks (#10608)

* adds error message for --no-ancient-blocks, closes #10261

* Apply suggestions from code review

Co-Authored-By: seunlanlege <seunlanlege@gmail.com>

* Constantinople HF on POA Core (#10606)

* Constantinople HF on POA Core

Plan Constantinople/St.Petersfork HF on POA Core network at block 8582254.
Original PR in POA repository: https://github.com/poanetwork/poa-chain-spec/pull/110

* Remove extra empty line

* evm: add some mulmod benches (#10600)

* evm: add blockhash_mulmod bench

* evm: use num-bigint for mod ops

* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length (#10605)

* Update kovan.json to switch validator set to POA Consensus Contracts (#10628)

* Fix publish docs (#10635)

* Fix publish docs

* this never should be forced, either way compiling previous versions will produce outdated docs

* fix array, var was moved to the group project global variables list

* Fix rinkeby petersburg fork (#10632)
2019-05-10 13:48:52 +02:00
Talha Cross b52ac20660
beta backports (#10576)
* Reject crazy timestamps instead of truncating.

* fix(light cull): poll light cull instead of timer (#10559)

* fix(light cull): poll light cull instead of timer

* fix(grumbles): remove error + updated docs

* fix(on-demand request): `expect()` reason

* docs(remove misleading info)
2019-04-08 11:44:10 +02:00
soc1c 3c85f29f11
version: betalize 2.5 2019-04-02 09:37:43 +02:00
1619 changed files with 188892 additions and 274032 deletions

View File

@ -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"]
# Link the C runtime statically ; https://github.com/openethereum/parity-ethereum/issues/6643
rustflags = ["-Ctarget-feature=+crt-static"]

2
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,2 @@
# Reformat the source code
610d9baba4af83b5767c659ca2ccfed337af1056

View File

@ -2,11 +2,11 @@
## 1. Purpose
A primary goal of Parity is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
A primary goal of OpenEthereum is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
We invite all those who participate in Parity to help us create safe and positive experiences for everyone.
We invite all those who participate in OpenEthereum to help us create safe and positive experiences for everyone.
## 2. Open Source Citizenship
@ -63,7 +63,7 @@ Additionally, community organizers are available to help community members engag
## 7. Addressing Grievances
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Parity Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify OpenEthereum Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
## 8. Scope
@ -73,7 +73,7 @@ This code of conduct and its related procedures also applies to unacceptable beh
## 9. Contact info
You can contact Parity via Email: community@parity.io
You can contact OpenEthereum via Email: community@parity.io
## 10. License and attribution

View File

@ -2,7 +2,7 @@
## Do you have a question?
Check out our [Basic Usage](https://wiki.parity.io/Basic-Usage), [Configuration](https://wiki.parity.io/Configuring-Parity-Ethereum), and [FAQ](https://wiki.parity.io/FAQ) articles on our [wiki](https://wiki.parity.io/)!
Check out our [Beginner Introduction](https://openethereum.github.io/Beginner-Introduction), [Configuration](https://openethereum.github.io//Configuring-OpenEthereum), and [FAQ](https://openethereum.github.io/FAQ) articles on our [wiki](https://openethereum.github.io/)!
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
@ -10,11 +10,11 @@ See also frequently asked questions [tagged with `parity`](https://ethereum.stac
Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead.
Otherwise, just create a [new issue](https://github.com/paritytech/parity-ethereum/issues/new) in our repository and state:
Otherwise, just create a [new issue](https://github.com/openethereum/openethereum/issues/new) in our repository and state:
- What's your Parity Ethereum version?
- What's your OpenEthereum version?
- What's your operating system and version?
- How did you install Parity Ethereum?
- How did you install OpenEthereum?
- Is your node fully synchronized?
- Did you try turning it off and on again?
@ -22,11 +22,11 @@ Also, try to include **steps to reproduce** the issue and expand on the **actual
## Contribute!
If you would like to contribute to Parity Ethereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity-ethereum/compare).
If you would like to contribute to OpenEthereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/openethereum/openethereum/compare).
### Labels & Milestones
We use [labels](https://github.com/paritytech/parity-ethereum/labels) to manage PRs and issues and communicate the state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in [milestones](https://github.com/paritytech/parity-ethereum/milestones). Best way to get started is to a pick a ticket from the current milestone tagged [`easy`](https://github.com/paritytech/parity-ethereum/labels/Q2-easy%20%F0%9F%92%83) and get going, or [`mentor`](https://github.com/paritytech/parity-ethereum/labels/Q1-mentor%20%F0%9F%95%BA) and get in contact with the mentor offering their support on that larger task.
We use [labels](https://github.com/openethereum/openethereum/labels) to manage PRs and issues and communicate the state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in [milestones](https://github.com/openethereum/openethereum/milestones). Best way to get started is to a pick a ticket from the current milestone tagged [`easy`](https://github.com/openethereum/openethereum/labels/Q2-easy%20%F0%9F%92%83) and get going, or [`mentor`](https://github.com/openethereum/openethereum/labels/Q1-mentor%20%F0%9F%95%BA) and get in contact with the mentor offering their support on that larger task.
### Rules
@ -35,22 +35,22 @@ There are a few basic ground-rules for contributors (including the maintainer(s)
* **No pushing directly to the master branch**.
* **All modifications** must be made in a **pull-request** to solicit feedback from other contributors.
* Pull-requests cannot be merged before CI runs green and two reviewers have given their approval.
* Contributors should adhere to the [Parity Ethereum Style Guide](https://wiki.parity.io/Parity-Ethereum-Style-Guide).
* All code changed should be formated by running `cargo fmt -- --config=merge_imports=true`
### Recommendations
* **Non-master branch names** *should* be prefixed with a short name moniker, followed by the associated Github Issue ID (if any), and a brief description of the task using the format `<GITHUB_USERNAME>-<ISSUE_ID>-<BRIEF_DESCRIPTION>` (e.g. `gavin-123-readme`). The name moniker helps people to inquiry about their unfinished work, and the GitHub Issue ID helps your future self and other developers (particularly those who are onboarding) find out about and understand the original scope of the task, and where it fits into Parity Ethereum [Projects](https://github.com/paritytech/parity-ethereum/projects).
* **Non-master branch names** *should* be prefixed with a short name moniker, followed by the associated Github Issue ID (if any), and a brief description of the task using the format `<GITHUB_USERNAME>-<ISSUE_ID>-<BRIEF_DESCRIPTION>` (e.g. `gavin-123-readme`). The name moniker helps people to inquiry about their unfinished work, and the GitHub Issue ID helps your future self and other developers (particularly those who are onboarding) find out about and understand the original scope of the task, and where it fits into Parity Ethereum [Projects](https://github.com/openethereum/openethereum/projects).
* **Remove stale branches periodically**
### Preparing Pull Requests
* If your PR does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged [`insubstantial`](https://github.com/paritytech/parity-ethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22).
* If your PR does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged [`insubstantial`](https://github.com/openethereum/openethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22).
* Once a PR is ready for review please add the [`pleasereview`](https://github.com/paritytech/parity-ethereum/pulls?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+label%3A%22A0-pleasereview+%F0%9F%A4%93%22+) label.
* Once a PR is ready for review please add the [`pleasereview`](https://github.com/openethereum/openethereum/pulls?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+label%3A%22A0-pleasereview+%F0%9F%A4%93%22+) label.
### Reviewing Pull Requests*:
* At least two reviewers are required to review PRs (even for PRs tagged [`insubstantial`](https://github.com/paritytech/parity-ethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22)).
* At least two reviewers are required to review PRs (even for PRs tagged [`insubstantial`](https://github.com/openethereum/openethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22)).
When doing a review, make sure to look for any:

View File

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

View File

@ -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!
✄ -----------------------------------------------------------------------------

View File

@ -0,0 +1,33 @@
name: Build and Test Suite on Windows
on:
push:
branches:
- main
- dev
jobs:
build-tests:
name: Test and Build
strategy:
matrix:
platform:
- windows2019 # custom runner
toolchain:
- 1.52.1
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources
uses: actions/checkout@main
with:
submodules: true
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
profile: minimal
override: true
- name: Build tests
uses: actions-rs/cargo@v1
with:
command: test
args: --locked --all --release --features "json-tests" --verbose --no-run

40
.github/workflows/build-test.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Build and Test Suite
on:
pull_request:
push:
branches:
- main
- dev
jobs:
build-tests:
name: Test and Build
strategy:
matrix:
platform:
- ubuntu-16.04
- macos-latest
toolchain:
- 1.52.1
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources
uses: actions/checkout@main
with:
submodules: true
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
profile: minimal
override: true
- name: Build tests
uses: actions-rs/cargo@v1
with:
command: test
args: --locked --all --release --features "json-tests" --verbose --no-run
- name: Run tests for ${{ matrix.platform }}
uses: actions-rs/cargo@v1
with:
command: test
args: --locked --all --release --features "json-tests" --verbose

285
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,285 @@
name: Build Release Suite
on:
push:
tags:
- v*
# Global vars
env:
AWS_REGION: "us-east-1"
AWS_S3_ARTIFACTS_BUCKET: "openethereum-releases"
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
jobs:
build:
name: Build Release
strategy:
matrix:
platform:
- ubuntu-16.04
- macos-latest
toolchain:
- 1.52.1
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources
uses: actions/checkout@main
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
profile: minimal
override: true
# ==============================
# Windows Build
# ==============================
# - name: Install LLVM for Windows
# if: matrix.platform == 'windows2019'
# run: choco install llvm
# - name: Build OpenEthereum for Windows
# if: matrix.platform == 'windows2019'
# run: sh scripts/actions/build-windows.sh ${{matrix.platform}}
# - name: Upload Windows build
# uses: actions/upload-artifact@v2
# if: matrix.platform == 'windows2019'
# with:
# name: windows-artifacts
# path: artifacts
# ==============================
# Linux/Macos Build
# ==============================
- name: Build OpenEthereum for ${{matrix.platform}}
if: matrix.platform != 'windows2019'
run: sh scripts/actions/build-linux.sh ${{matrix.platform}}
- name: Upload Linux build
uses: actions/upload-artifact@v2
if: matrix.platform == 'ubuntu-16.04'
with:
name: linux-artifacts
path: artifacts
- name: Upload MacOS build
uses: actions/upload-artifact@v2
if: matrix.platform == 'macos-latest'
with:
name: macos-artifacts
path: artifacts
zip-artifacts-creator:
name: Create zip artifacts
needs: build
runs-on: ubuntu-16.04
steps:
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
# ==============================
# Create ZIP files
# ==============================
# - name: Download Windows artifacts
# uses: actions/download-artifact@v2
# with:
# name: windows-artifacts
# path: windows-artifacts
- name: Download Linux artifacts
uses: actions/download-artifact@v2
with:
name: linux-artifacts
path: linux-artifacts
- name: Download MacOS artifacts
uses: actions/download-artifact@v2
with:
name: macos-artifacts
path: macos-artifacts
- name: Display structure of downloaded files
run: ls
- name: Create zip Linux
id: create_zip_linux
run: |
cd linux-artifacts/
zip -rT openethereum-linux-${{ env.RELEASE_VERSION }}.zip *
ls openethereum-linux-${{ env.RELEASE_VERSION }}.zip
cd ..
mv linux-artifacts/openethereum-linux-${{ env.RELEASE_VERSION }}.zip .
echo "Setting outputs..."
echo ::set-output name=LINUX_ARTIFACT::openethereum-linux-${{ env.RELEASE_VERSION }}.zip
echo ::set-output name=LINUX_SHASUM::$(shasum -a 256 openethereum-linux-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}')
- name: Create zip MacOS
id: create_zip_macos
run: |
cd macos-artifacts/
zip -rT openethereum-macos-${{ env.RELEASE_VERSION }}.zip *
ls openethereum-macos-${{ env.RELEASE_VERSION }}.zip
cd ..
mv macos-artifacts/openethereum-macos-${{ env.RELEASE_VERSION }}.zip .
echo "Setting outputs..."
echo ::set-output name=MACOS_ARTIFACT::openethereum-macos-${{ env.RELEASE_VERSION }}.zip
echo ::set-output name=MACOS_SHASUM::$(shasum -a 256 openethereum-macos-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}')
# - name: Create zip Windows
# id: create_zip_windows
# run: |
# cd windows-artifacts/
# zip -rT openethereum-windows-${{ env.RELEASE_VERSION }}.zip *
# ls openethereum-windows-${{ env.RELEASE_VERSION }}.zip
# cd ..
# mv windows-artifacts/openethereum-windows-${{ env.RELEASE_VERSION }}.zip .
# echo "Setting outputs..."
# echo ::set-output name=WINDOWS_ARTIFACT::openethereum-windows-${{ env.RELEASE_VERSION }}.zip
# echo ::set-output name=WINDOWS_SHASUM::$(shasum -a 256 openethereum-windows-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}')
# =======================================================================
# Upload artifacts
# This is required to share artifacts between different jobs
# =======================================================================
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
path: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
path: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
# - name: Upload artifacts
# uses: actions/upload-artifact@v2
# with:
# name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
# path: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
# =======================================================================
# Upload artifacts to S3
# This is required by some software distribution systems which require
# artifacts to be downloadable, like Brew on MacOS.
# =======================================================================
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Copy files to S3 with the AWS CLI
run: |
# Deploy zip artifacts to S3 bucket to a directory whose name is the tagged release version.
# Deploy macos binary artifact (if required, add more `aws s3 cp` commands to deploy specific OS versions)
aws s3 cp macos-artifacts/openethereum s3://${{ env.AWS_S3_ARTIFACTS_BUCKET }}/${{ env.RELEASE_VERSION }}/macos/ --region ${{ env.AWS_REGION }}
outputs:
linux-artifact: ${{ steps.create_zip_linux.outputs.LINUX_ARTIFACT }}
linux-shasum: ${{ steps.create_zip_linux.outputs.LINUX_SHASUM }}
macos-artifact: ${{ steps.create_zip_macos.outputs.MACOS_ARTIFACT }}
macos-shasum: ${{ steps.create_zip_macos.outputs.MACOS_SHASUM }}
# windows-artifact: ${{ steps.create_zip_windows.outputs.WINDOWS_ARTIFACT }}
# windows-shasum: ${{ steps.create_zip_windows.outputs.WINDOWS_SHASUM }}
draft-release:
name: Draft Release
needs: zip-artifacts-creator
runs-on: ubuntu-16.04
steps:
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
# ==============================
# Download artifacts
# ==============================
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
# - name: Download artifacts
# uses: actions/download-artifact@v2
# with:
# name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
- name: Display structure of downloaded files
run: ls
# ==============================
# Create release draft
# ==============================
- name: Create Release Draft
id: create_release_draft
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.ref }}
release_name: OpenEthereum ${{ github.ref }}
body: |
This release contains <ADD_TEXT>
| System | Architecture | Binary | Sha256 Checksum |
|:---:|:---:|:---:|:---|
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/apple.png" alt="Apple Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | x64 | [${{ needs.zip-artifacts-creator.outputs.macos-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.macos-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.macos-shasum }}` |
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/linux.png" alt="Linux Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | x64 | [${{ needs.zip-artifacts-creator.outputs.linux-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.linux-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.linux-shasum }}` |
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/windows.png" alt="Windows Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | x64 | [${{ needs.zip-artifacts-creator.outputs.windows-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.windows-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.windows-shasum }}` |
| | | | |
| **System** | **Option** | - | **Resource** |
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/settings.png" alt="Settings Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | Docker | - | [hub.docker.com/r/openethereum/openethereum](https://hub.docker.com/r/openethereum/openethereum) |
draft: true
prerelease: true
- name: Upload Release Asset - Linux
id: upload_release_asset_linux
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./openethereum-linux-${{ env.RELEASE_VERSION }}.zip
asset_name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
asset_content_type: application/zip
- name: Upload Release Asset - MacOS
id: upload_release_asset_macos
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./openethereum-macos-${{ env.RELEASE_VERSION }}.zip
asset_name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
asset_content_type: application/zip
# - name: Upload Release Asset - Windows
# id: upload_release_asset_windows
# uses: actions/upload-release-asset@v1
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
# asset_path: ./openethereum-windows-${{ env.RELEASE_VERSION }}.zip
# asset_name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
# asset_content_type: application/zip

50
.github/workflows/check.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: Check
on:
pull_request:
push:
branches:
- main
- dev
jobs:
check:
name: Check
runs-on: ubuntu-16.04
steps:
- name: Checkout sources
uses: actions/checkout@main
with:
submodules: true
- name: Install 1.52.1 toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: 1.52.1
profile: minimal
override: true
- name: Run cargo check 1/3
uses: actions-rs/cargo@v1
with:
command: check
args: --locked --no-default-features --verbose
- name: Run cargo check 2/3
uses: actions-rs/cargo@v1
with:
command: check
args: --locked --manifest-path crates/runtime/io/Cargo.toml --no-default-features --verbose
- name: Run cargo check 3/3
uses: actions-rs/cargo@v1
with:
command: check
args: --locked --manifest-path crates/runtime/io/Cargo.toml --features "mio" --verbose
- name: Run cargo check evmbin
uses: actions-rs/cargo@v1
with:
command: check
args: --locked -p evmbin --verbose
- name: Run cargo check benches
uses: actions-rs/cargo@v1
with:
command: check
args: --locked --all --benches --verbose
- name: Run validate chainspecs
run: ./scripts/actions/validate-chainspecs.sh

View File

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

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

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

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

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

20
.github/workflows/fmt.yml vendored Normal file
View File

@ -0,0 +1,20 @@
on: [push, pull_request]
name: rustfmt
jobs:
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.52.1
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check --config merge_imports=true

4
.gitignore vendored
View File

@ -38,8 +38,10 @@ node_modules
# Build artifacts
out/
parity-clib-examples/cpp/build/
.vscode
rls/
/parity.*
# cargo remote artifacts
remote-target

View File

@ -1,356 +0,0 @@
stages:
- test
- build
- publish
- optional
image: ${REGISTRY}/parity-ci-linux:latest
variables:
GIT_STRATEGY: fetch
GIT_SUBMODULE_STRATEGY: recursive
GIT_DEPTH: 3
CI_SERVER_NAME: "GitLab CI"
CARGO_HOME: "/ci-cache/${CI_PROJECT_NAME}/cargo/${CI_JOB_NAME}"
CARGO_TARGET: x86_64-unknown-linux-gnu
CARGO_INCREMENTAL: 0
REGISTRY: registry.parity.io/parity/infrastructure/scripts
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
only: &releaseable_branches
- stable
- tags
- schedules
.collect_artifacts: &collect_artifacts
artifacts:
name: "${CI_JOB_NAME}_${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}"
when: on_success
expire_in: 1 mos
paths:
- artifacts/
- tools/
.docker-cache-status: &docker-cache-status
dependencies: []
interruptible: true
before_script:
- rustup show
- cargo --version
retry:
max: 2
when:
- runner_system_failure
- unknown_failure
- api_failure
tags:
- linux-docker
.build-on-linux: &build-on-linux
stage: build
<<: *docker-cache-status
<<: *collect_artifacts
script:
- scripts/gitlab/build-linux.sh
after_script:
- mkdir -p tools
- cp -r scripts/docker/hub/* ./tools
- cp scripts/gitlab/publish-snap.sh ./tools
- cp scripts/gitlab/publish-onchain.sh ./tools
- cp scripts/gitlab/safe-curl.sh ./tools
- echo v"$(sed -r -n '1,/^version/s/^version\s*=\s*"([^"]+)".*$/\1/p' Cargo.toml)" |
tee ./tools/VERSION
- echo "$(sed -r -n '1,/^track/s/^track\s*=\s*"([^"]+)".*$/\1/p' ./util/version/Cargo.toml)" |
tee ./tools/TRACK
cargo-check 0 3:
stage: test
<<: *docker-cache-status
script:
- time cargo check --target $CARGO_TARGET --locked --no-default-features --verbose --color=always
- sccache --show-stats
cargo-check 1 3:
stage: test
<<: *docker-cache-status
script:
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features --verbose --color=always
- sccache --show-stats
cargo-check 2 3:
stage: test
<<: *docker-cache-status
script:
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose --color=always
- sccache --show-stats
cargo-check-evmbin:
stage: test
<<: *docker-cache-status
script:
- time cargo check -p evmbin --target $CARGO_TARGET --locked --verbose --color=always
- sccache --show-stats
cargo-check-benches:
stage: test
<<: *docker-cache-status
script:
- time cargo check --all --benches --target $CARGO_TARGET --locked --verbose --color=always
- sccache --show-stats
cargo-audit:
stage: test
<<: *docker-cache-status
script:
- cargo audit
allow_failure: true # failed cargo audit shouldn't prevent a PR from being merged
validate-chainspecs:
stage: test
<<: *docker-cache-status
script:
- ./scripts/gitlab/validate-chainspecs.sh
test-linux:
stage: build
<<: *docker-cache-status
script:
- ./scripts/gitlab/test-linux.sh stable
test-linux-beta:
stage: build
only: *releaseable_branches
<<: *docker-cache-status
script:
- ./scripts/gitlab/test-linux.sh beta
test-linux-nightly:
stage: build
only: *releaseable_branches
<<: *docker-cache-status
script:
- ./scripts/gitlab/test-linux.sh nightly
allow_failure: true
build-linux:
<<: *build-on-linux
only: *releaseable_branches
build-linux-i386:
<<: *build-on-linux
only: *releaseable_branches
image: ${REGISTRY}/parity-ci-i386:latest
variables:
CARGO_TARGET: i686-unknown-linux-gnu
build-linux-arm64:
<<: *build-on-linux
only: *releaseable_branches
image: ${REGISTRY}/parity-ci-arm64:latest
variables:
CARGO_TARGET: aarch64-unknown-linux-gnu
build-linux-armhf:
<<: *build-on-linux
only: *releaseable_branches
image: ${REGISTRY}/parity-ci-armhf:latest
variables:
CARGO_TARGET: armv7-unknown-linux-gnueabihf
build-darwin:
stage: build
<<: *collect_artifacts
only: *releaseable_branches
variables:
CARGO_TARGET: x86_64-apple-darwin
CARGO_HOME: "${CI_PROJECT_DIR}/.cargo"
script:
- scripts/gitlab/build-linux.sh
tags:
- rust-osx
build-windows:
stage: build
<<: *collect_artifacts
only: *releaseable_branches
variables:
CARGO_TARGET: x86_64-pc-windows-msvc
CARGO_HOME: "C:/ci-cache/parity-ethereum/cargo/$CI_JOB_NAME"
GIT_SUBMODULE_STRATEGY: none
script:
- sh scripts/gitlab/build-windows.sh
tags:
- rust-windows
publish-docker:
stage: publish
only: *releaseable_branches
except:
- nightly
when: manual
dependencies:
- build-linux
environment:
name: parity-build
cache: {}
image: docker:stable
services:
- docker:dind
variables:
GIT_STRATEGY: none
DOCKER_HOST: tcp://localhost:2375
DOCKER_DRIVER: overlay2
GIT_STRATEGY: none
# DOCKERFILE: tools/Dockerfile
# CONTAINER_IMAGE: parity/parity
script:
- ./tools/publish-docker.sh
tags:
- kubernetes-parity-build
publish-snap-nightly: &publish-snap
stage: publish
only:
- nightly
image: snapcore/snapcraft
variables:
GIT_STRATEGY: none
BUILD_ARCH: amd64
cache: {}
dependencies:
- build-linux
tags:
- linux-docker
script:
- ./tools/publish-snap.sh
publish-snap-manually:
<<: *publish-snap
only: *releaseable_branches
when: manual
publish-snap-i386-nightly: &publish-snap-i386
<<: *publish-snap
variables:
BUILD_ARCH: i386
CARGO_TARGET: i686-unknown-linux-gnu
dependencies:
- build-linux-i386
publish-snap-i386-manually:
<<: *publish-snap-i386
only: *releaseable_branches
when: manual
publish-snap-arm64-nightly: &publish-snap-arm64
<<: *publish-snap
variables:
BUILD_ARCH: arm64
CARGO_TARGET: aarch64-unknown-linux-gnu
dependencies:
- build-linux-arm64
publish-snap-arm64-manually:
<<: *publish-snap-arm64
only: *releaseable_branches
when: manual
publish-snap-armhf-nightly: &publish-snap-armhf
<<: *publish-snap
variables:
BUILD_ARCH: armhf
CARGO_TARGET: armv7-unknown-linux-gnueabihf
dependencies:
- build-linux-armhf
publish-snap-armhf-manually:
<<: *publish-snap-armhf
only: *releaseable_branches
when: manual
publish-onchain-nightly: &publish-onchain
stage: publish
only:
- nightly
cache: {}
variables:
GIT_STRATEGY: none
dependencies:
- build-linux
- build-darwin
- build-windows
script:
- ./tools/publish-onchain.sh
tags:
- linux-docker
publish-onchain-manually:
<<: *publish-onchain
only: *releaseable_branches
when: manual
publish-release-awss3-nightly: &publish-release-awss3
image: ${REGISTRY}/awscli:latest
stage: publish
only:
- nightly
variables:
GIT_STRATEGY: none
cache: {}
dependencies:
- build-linux
- build-darwin
- build-windows
script:
- echo "__________Push binaries to AWS S3____________"
- case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in
(stable|nightly)
export BUCKET=releases.parity.io/ethereum;
;;
(*)
export BUCKET=builds-parity;
;;
esac
- aws s3 sync ./artifacts s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/
- echo "__________Read from S3____________"
- aws s3 ls s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} --recursive --human-readable --summarize
tags:
- linux-docker
publish-release-awss3-manually:
<<: *publish-release-awss3
only: *releaseable_branches
when: manual
publish-docs:
stage: publish
image: ${REGISTRY}/parity-ci-docs:latest
only:
- tags
except:
- nightly
when: manual
cache: {}
dependencies: []
script:
- scripts/gitlab/publish-docs.sh
tags:
- linux-docker
allow_failure: true
publish-av-whitelist:
stage: publish
variables:
GIT_STRATEGY: none
only: *releaseable_branches
except:
- nightly
when: manual
cache: {}
dependencies:
- build-windows
script:
- scripts/gitlab/publish-av-whitelists.sh
tags:
- linux-docker

8
.gitmodules vendored
View File

@ -1,7 +1,3 @@
[submodule "ethcore/res/ethereum/tests"]
path = ethcore/res/ethereum/tests
[submodule "crates/ethcore/res/json_tests"]
path = crates/ethcore/res/json_tests
url = https://github.com/ethereum/tests.git
branch = develop
[submodule "ethcore/res/wasm-tests"]
path = ethcore/res/wasm-tests
url = https://github.com/paritytech/wasm-tests

View File

@ -1,397 +1,208 @@
## Parity-Ethereum [v2.7.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.1)
Parity Ethereum v2.7.2-stable is a patch version release of parity-ethereum.
Starting in the 2.7.x series of releases, parity-ethereum is switching to a single `stable` release
track. As a result, any clients that currently receive updates from the `beta`
track should switch to the `stable` track.
Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only.
## OpenEthereum v3.3.3
The full list of included changes:
Enhancements:
* Implement eip-3607 (#593)
* backwards compatible call_type creation_method (#11450 + #11455)
* chore: remove unused dependencies (#11432)
* Cargo.lock: new lockfile format (#11448)
* rlp_derive: cleanup (#11446)
* Avoid long state queries when serving GetNodeData requests (#11444)
* update kvdb-rocksdb to 0.4 (#11442)
* Remove dead bootnodes, add new geth bootnodes (#11441)
* goerli: replace foundation bootnode (#11433)
* fix: export hardcoded sync format (#11416)
* verification: fix race same block + misc (#11400)
* update classic testnet bootnodes (#11398)
* gcc to clang (#11453)
Bug fixes:
* Add type field for legacy transactions in RPC calls (#580)
* Makes eth_mining to return False if not is not allowed to seal (#581)
* Made nodes data concatenate as RLP sequences instead of bytes (#598)
## Parity-Ethereum [v2.7.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.1)
Parity Ethereum v2.7.1-stable is a patch version release of parity-ethereum.
Starting in the 2.7.x series of releases, parity-ethereum is switching to a single `stable` release
track. As a result, any clients that currently receive updates from the `beta`
track should switch to the `stable` track.
Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only.
## OpenEthereum v3.3.2
The full list of included changes from `v2.7.0` to `v2.7.1`:
Enhancements:
* London hardfork block: Sokol (24114400)
* Revert "Distinguish between `create` and `create2` (#11311)" (#11427)
Bug fixes:
* Fix for maxPriorityFeePerGas overflow
## Parity-Ethereum [v2.7.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.0)
## OpenEthereum v3.3.1
Parity Ethereum v2.7.0-stable is a minor version release of parity-ethereum. As
of this release, parity-ethereum is switching to a single `stable` release
track. As a result, any clients that currently receive updates from the `beta`
track should switch to the `stable` track.
Enhancements:
* Add eth_maxPriorityFeePerGas implementation (#570)
* Add a bootnode for Kovan
The full list of included changes from `v2.5-stable` to `v2.7-stable` (the
`v2.6-beta` branch will already include some of these changes):
Bug fixes:
* Fix for modexp overflow in debug mode (#578)
* Update POA bootnodes (#11411)
* Update ProgPoW to 0.9.3 (#11407)
* Add EtherCore support (#11402)
* json-tests: Fix compile error (#11384)
* ethcore/res: fix ethereum classic chainspec blake2_f activation block num (#11391)
* Switching to stable-track (#11377)
* Update copyright notice 2020 (#11386)
* miner: fix deprecation warning Error::description (#11380)
* Fix Aztlan hard fork issues (#11347)
* authority_round: Fix next_step_time_duration. (#11379)
* Set the block gas limit to the value returned by a contract call (#10928)
* [Trace] Distinguish between `create` and `create2` (#11311)
* fix cargo audit (#11378)
* Fix esoteric test config variable (#11292)
* Rip out the C and Java bindings (#11346)
* Encapsulate access to the client for secret store (#11232)
* Forward-port #11356 (#11359)
* Fix error message typo (#11363)
* [util/migration]: remove needless `static` bounds (#11348)
* Replace stale boot nodes with latest list (#11351)
* Update to latest `kvdb-*`: no default column, DBValue is Vec (#11312)
* we do not profit from incremental now (#11302)
* update autoupdate fork blocks for nightly (#11308)
* Add Nat PMP method to P2P module (#11210)
* Add randomness contract support to AuthorityRound. (#10946)
* ethcore/res: activate ecip-1061 on kotti and mordor (#11338)
* tx-q: enable basic verification of local transactions (#11332)
* remove null signatures (#11335)
* ethcore/res: activate agharta on classic 9573000 (#11331)
* [secretstore] migrate to version 4 (#11322)
* Enable EIP-2384 for ice age hard fork (#11281)
* Fix atomicity violation in network-devp2p (#11277)
* Istanbul activation on xDai (#11299)
* Istanbul activation on POA Core (#11298)
* Adds support for ipc socket permissions (#11273)
* Add check for deserialising hex values over U256 limit (#11309)
* validate-chainspecs: check istanbul eips are in the foundation spec (#11305)
* [chainspec]: add `eip1344_transition` for istanbul (#11301)
* only add transactions to signing-queue if it is enabled (#11272)
* Use upstream rocksdb (#11248)
* Treat only blocks in queue as synced (#11264)
* add support for evan.network chains (#11289)
* Add benchmarks and tests for RlpNodeCodec decoding (#11287)
* upgrade vergen to 3.0 (#11293)
* interruptible test and build jobs (#11294)
* Istanbul HF on POA Sokol (#11282)
* [ethcore]: apply filter when `PendingSet::AlwaysQueue` in `ready_transactions_filtered` (#11227)
* Update lib.rs (#11286)
* Don't prune ancient state when instantiating a Client (#11270)
* fixed verify_uncles error type (#11276)
* ethcore: fix rlp deprecation warnings (#11280)
* Upgrade trie-db to 0.16.0. (#11274)
* Clarify what first_block `None` means (#11269)
* removed redundant VMType enum with one variant (#11266)
* Ensure jsonrpc threading settings are sane (#11267)
* Return Ok(None) when the registrar contract returns empty slice (#11257)
* Add a benchmark for snapshot::account::to_fat_rlps() (#11185)
* Fix misc compile warnings (#11258)
* simplify verification (#11249)
* update ropsten forkCanonHash, forkBlock (#11247)
* Make InstantSeal Instant again (#11186)
* ropsten #6631425 foundation #8798209 (#11201)
* Update list of bootnodes for xDai chain (#11236)
* ethcore/res: add mordor testnet configuration (#11200)
* [chain specs]: activate `Istanbul` on mainnet (#11228)
* [builtin]: support `multiple prices and activations` in chain spec (#11039)
* Insert explicit warning into the panic hook (#11225)
* Snapshot restoration overhaul (#11219)
* Fix docker centos build (#11226)
* retry on gitlab system failures (#11222)
* Update bootnodes. (#11203)
* Use provided usd-per-eth value if an endpoint is specified (#11209)
* Use a lock instead of atomics for snapshot Progress (#11197)
* [informant]: `MillisecondDuration` -> `as_millis()` (#11211)
* Step duration map configuration parameter ported from the POA Network fork (#10902)
* Upgrade jsonrpc to latest (#11206)
* [export hardcoded sync]: use debug for `H256` (#11204)
* Pause pruning while snapshotting (#11178)
* Type annotation for next_key() matching of json filter options (#11192)
* Crypto primitives removed from ethkey (#11174)
* Made ecrecover implementation trait public (#11188)
* Remove unused macro_use. (#11191)
* [dependencies]: jsonrpc `14.0.1` (#11183)
* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179)
* [dependencies] bump rand 0.7 (#11022)
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
* TxPermissions ver 3: gas price & data (#11170)
* [ethash] chainspec validate `ecip1017EraRounds` non-zero (#11123)
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
* Aura: Report malice on sibling blocks from the same validator (#11160)
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
* Cleanup stratum a bit (#11161)
* [keccak-hasher]: rust2018 (#11163)
* Upgrade to jsonrpc v14 (#11151)
* Secret store: fix Instant::now() related race in net_keep_alive (#11155)
* RPC method for clearing the engine signer (#10920)
* Use TryFrom instead of From+panic for Builtin (#11140)
* Fix sccache statistics (#11145)
* Update ethereum types to 0.8.0 version (#11139)
* [json]: add docs to `hardfork specification` (#11138)
* ServiceTransactionChecker::refresh_cache: allow registrar unavailable (#11126)
* Fix some random typos, formatting/whitespace (#11128)
* Refactor parity_listStorageKeys with count parameter optional (#11124)
* Make EIP712Domain Fields Optional (#11103)
* EIP-712: bump version in prep for publishing (#11106)
* move StateResult to `common-types` (#11121)
* Deduplicate registrar contract & calling logic (#11110)
* Refactor return type of `BlockChainClient::code` #7098 (#11102)
* Switching sccache from local to Redis (#10971)
* SIMD Implementation for EIP-152 (#11056)
* Fix deprecated trait objects without an explicit `dyn` (#11112)
* [spec] fix rinkeby spec (#11108)
* Update to latest jsonrpc (#11111)
* use images from our registry (#11105)
* Correct EIP-712 encoding (#11092)
* [CI] check evmbin build (#11096)
* Update `kvdb`, `kvdb-rocksdb` and `h2` (#11091)
* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086)
* Update JSON tests to d4f86ecf4aa7c (#11054)
* fix(network): typo (#11088)
* [ethash] remove manual unrolling (#11069)
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)
* [sync]: rust 2018 (#11067)
* [ethcore]: move client test types to test-helpers (#11062)
* [sync]: remove unused dependencies or make dev (#11061)
* [ethcore]: reduce re-exports (#11059)
* [evmbin] fix time formatting (#11060)
* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053)
* cargo update -p eth-secp256k1 (#11052)
* ethcore: remove `test-helper feat` from build (#11047)
* Include test-helpers from ethjson (#11045)
* [ethcore]: cleanup dependencies (#11043)
* add more tx tests (#11038)
* Fix parallel transactions race-condition (#10995)
* [ethcore]: make it compile without `test-helpers` feature (#11036)
* Benchmarks for block verification (#11035)
* Move snapshot related traits to their proper place (#11012)
* cleanup json crate (#11027)
* [spec] add istanbul test spec (#11033)
* [json-spec] make blake2 pricing spec more readable (#11034)
* Add blake2_f precompile (#11017)
* Add new line after writing block to hex file. (#10984)
* fix: remove unused error-chain (#11028)
* fix: remove needless use of itertools (#11029)
* Convert `std::test` benchmarks to use Criterion (#10999)
* Fix block detail updating (#11015)
* [trace] introduce trace failed to Ext (#11019)
* cli: update usage and version headers (#10924)
* [private-tx] remove unused rand (#11024)
* Extract snapshot to own crate (#11010)
* Edit publish-onchain.sh to use https (#11016)
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
* Fix deadlock in `network-devp2p` (#11013)
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191)
* EIP 1884 Re-pricing of trie-size dependent operations (#10992)
* xDai chain support and nodes list update (#10989)
* [trace] check mem diff within range (#11002)
* EIP-1344 Add CHAINID op-code (#10983)
* Make ClientIoMessage generic over the Client (#10981)
* bump spin to 0.5.2 (#10996)
* fix compile warnings (#10993)
* Fix compilation on recent nightlies (#10991)
* [ipfs] Convert to edition 2018 (#10979)
* Extract spec to own crate (#10978)
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
* Extract engines to own crates (#10966)
* Configuration map of block reward contract addresses (#10875)
* Add a 2/3 quorum option to Authority Round. (#10909)
* Fix rlp decode for inline trie nodes. (#10980)
* Private contract migration and offchain state sync (#10748)
* manual publish jobs for releases, no changes for nightlies (#10977)
* Extract the Engine trait (#10958)
* Better error message for rpc gas price errors (#10931)
* [.gitlab.yml] cargo check ethcore benches (#10965)
* Verify transaction against its block during import (#10954)
* [evmbin] fix compilation (#10976)
* Update to latest trie version. (#10972)
* [blooms-db] Fix benchmarks (#10974)
* Fix ethcore/benches build. (#10964)
* tx-pool: accept local tx with higher gas price when pool full (#10901)
* Disable unsyncable expanse chain (#10926)
* Extract Machine from ethcore (#10949)
* removed redundant state_root function from spec, improve spec error types (#10955)
* Add support for Energy Web Foundation's new chains (#10957)
* [evmbin] add more tests to main.rs (#10956)
* Fix compiler warnings in util/io and upgrade to edition 2018 Upgrade mio to latest (#10953)
* unify loading spec && further spec cleanups (#10948)
* refactor: Refactor evmbin CLI (#10742)
* journaldb changes (#10929)
* Allow default block parameter to be blockHash (#10932)
* Enable sealing when engine is ready (#10938)
* Fix some warnings and typos. (#10941)
* Updated security@parity.io key (#10939)
* Change the return type of step_inner function. (#10940)
* get rid of hidden mutability of Spec (#10904)
* simplify BlockReward::reward implementation (#10906)
* Kaspersky AV whitelisting (#10919)
* additional arithmetic EVM opcode benchmarks (#10916)
* [Cargo.lock] cargo update -p crossbeam-epoch (#10921)
* Fixes incorrect comment. (#10913)
* Add file path to disk map write/read warnings (#10911)
* remove verify_transaction_unordered from engine (#10891)
* Avast whitelist script (#10900)
* cleanup ethcore ethereum module (#10899)
* Move more types out of ethcore (#10880)
* return block nonce when engine is clique (#10892)
* TransactionQueue::import accepts iterator (#10889)
* rename is_pruned to is_prunable (#10888)
* simplify create_address_scheme (#10890)
* Move DatabaseExtras back to trace (#10868)
* Update README.md and Changelogs (#10866)
* whisper is no longer a part of parity-ethereum repo (#10855)
* [ethash] remove mem::uninitialized (#10861)
* Docker images renaming (#10863)
* Move the substate module into ethcore/executive (#10867)
* Run cargo fix on a few of the worst offenders (#10854)
* removed redundant fork choice abstraction (#10849)
* Extract state-db from ethcore (#10858)
* Fix fork choice (#10837)
* Move more code into state-account (#10840)
* Remove compiler warning (#10865)
* [ethash] use static_assertions crate (#10860)
* EIP-1702: Generalized Account Versioning Scheme (#10771)
* ethcore-builtin (#10850)
* removed QueueError type (#10852)
* removed unused macros (#10851)
* bump crossbeam (#10848)
* removed unused trait PrivateNotify and unused Error types (#10847)
* make fn submit_seal more idiomatic (#10843)
* update parking-lot to 0.8 (#10845)
* Update version to 2.7.0 (#10846)
* update jsonrpc to 12.0 (#10841)
* Improve logging and cleanup in miner around block sealing (#10745)
* Extract AccountDB to account-db (#10839)
* test: Update Whisper test for invalid pool size (#10811)
* Extricate PodAccount and state Account to own crates (#10838)
* logs (#10817)
* refactor: whisper: Add type aliases and update rustdocs in message.rs (#10812)
* Break circular dependency between Client and Engine (part 1) (#10833)
* tests: Relates to #10655: Test instructions for Readme (#10835)
* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs (#10657)
* idiomatic changes to PodState (#10834)
* Allow --nat extip:your.host.here.org (#10830)
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
* Remove excessive warning (#10831)
* Fix typo in README.md (#10828)
* ethcore does not use byteorder (#10829)
* Better logging when backfilling ancient blocks fail (#10796)
* depends: Update wordlist to v1.3 (#10823)
* cargo update -p smallvec (#10822)
* replace memzero with zeroize crate (#10816)
* Don't repeat the logic from Default impl (#10813)
* removed additional_params method (#10818)
* Add Constantinople eips to the dev (instant_seal) config (#10809)
* removed redundant fmt::Display implementations (#10806)
* revert changes to .gitlab-ci.yml (#10807)
* Add filtering capability to `parity_pendingTransactions` (issue 8269) (#10506)
* removed EthEngine alias (#10805)
* wait a bit longer in should_check_status_of_request_when_its_resolved (#10808)
* Do not drop the peer with None difficulty (#10772)
* ethcore-bloom-journal updated to 2018 (#10804)
* ethcore-light uses bincode 1.1 (#10798)
* Fix a few typos and unused warnings. (#10803)
* updated project to ansi_term 0.11 (#10799)
* added new ropsten-bootnode and removed old one (#10794)
* updated price-info to edition 2018 (#10801)
* ethcore-network-devp2p uses igd 0.9 (#10797)
* updated parity-local-store to edition 2018 and removed redundant Error type (#10800)
* Cleanup unused vm dependencies (#10787)
* Removed redundant ethcore-service error type (#10788)
* Removed machine abstraction from ethcore (#10791)
* Updated blooms-db to rust 2018 and removed redundant deps (#10785)
* ethkey no longer uses byteorder (#10786)
* Log validator set changes in EpochManager (#10734)
* Treat empty account the same as non-exist accounts in EIP-1052 (#10775)
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
* Move Engine::register_client to be before other I/O handler registration (#10767)
* Print warnings when using dangerous settings for ValidatorSet (#10733)
* ethcore/res: activate atlantis classic hf on block 8772000 (#10766)
* refactor: Fix indentation (#10740)
* Updated Bn128PairingImpl to use optimized batch pairing (#10765)
* fix: aura don't add `SystemTime::now()` (#10720)
* Initialize private tx logger only if private tx functionality is enabled (#10758)
* Remove unused code (#10762)
* Remove calls to heapsize (#10432)
* [devp2p] Update to 2018 edition (#10716)
* Add a way to signal shutdown to snapshotting threads (#10744)
* Enable aesni (#10756)
* remove support of old SS db formats (#10757)
* [devp2p] Don't use `rust-crypto` (#10714)
* updater: fix static id hashes initialization (#10755)
* Use fewer threads for snapshotting (#10752)
* Die error_chain, die (#10747)
* Fix deprectation warnings on nightly (#10746)
* fix docker tags for publishing (#10741)
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet (#10705)
* ethcore: enable ECIP-1054 for classic (#10731)
* Stop breaking out of loop if a non-canonical hash is found (#10729)
* Refactor Clique stepping (#10691)
* Use RUSTFLAGS to set the optimization level (#10719)
* SecretStore: non-blocking wait of session completion (#10303)
* removed secret_store folder (#10722)
* SecretStore: expose restore_key_public in HTTP API (#10241)
* Revert "enable lto for release builds (#10717)" (#10721)
* enable lto for release builds (#10717)
* Merge `Notifier` and `TransactionsPoolNotifier` (#10591)
* [devp2p] Fix warnings and re-org imports (#10710)
* Upgrade ethereum types (#10670)
* introduce MissingParent Error, fixes #10699 (#10700)
* Update publishing (#10644)
* Upgrade to parity-crypto 0.4 (#10650)
* new image (#10673)
* Add SealingState; don't prepare block when not ready. (#10529)
* Fix compiler warning (that will become an error) (#10683)
* add_sync_notifier in EthPubSubClient holds on to a Client for too long (#10689)
* Don't panic if extra_data is longer than VANITY_LENGTH (#10682)
* docs: evmbin - Update Rust docs (#10658)
* Remove annoying compiler warnings (#10679)
* Reset blockchain properly (#10669)
* Remove support for hardware wallets (#10678)
* [CI] allow cargo audit to fail (#10676)
* docs: Add ProgPoW Rust docs to ethash module (#10653)
* fix: Move PR template into .github/ folder (#10663)
* docs: Add PR template (#10654)
* Trivial journal for private transactions (#10056)
* fix(compilation warnings) (#10649)
* [whisper] Move needed aes_gcm crypto in-crate (#10647)
* Adds parity_getRawBlockByNumber, parity_submitRawBlock (#10609)
* Fix rinkeby petersburg fork (#10632)
* ci: publish docs debug (#10638)
* Fix publish docs (#10635)
* Update kovan.json to switch validator set to POA Consensus Contracts (#10628)
* [ethcore] remove error_chain (#10616)
* Remove unused import (#10615)
* evm: add some mulmod benches (#10600)
* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length (#10605)
* Constantinople HF on POA Core (#10606)
* adds rpc error message for --no-ancient-blocks (#10608)
* Allow CORS requests in Secret Store API (#10584)
* update bootnodes (#10595)
* sccache logs to stdout (#10596)
* fix(whisper expiry): current time + work + ttl (#10587)
* CI improvements (#10579)
* Watch transactions pool (#10558)
* fix(evmbin): make benches compile again (#10586)
* fix issue with compilation when 'slow-blocks' feature enabled (#10585)
* Reject crazy timestamps instead of truncating. (#10574)
* Node table limiting and cache for node filter (#10288)
* fix(light cull): poll light cull instead of timer (#10559)
* Update Issue Template to direct security issue to email (#10562)
* RPC: Implements eth_subscribe("syncing") (#10311)
* Explicitly enable or disable Stratum in config file (Issue 9785) (#10521)
* version: bump master to 2.6 (#10560)
## OpenEthereum v3.3.0
Enhancements:
* Add `validateServiceTransactionsTransition` spec option to be able to enable additional checking of zero gas price transactions by block verifier
## OpenEthereum v3.3.0-rc.15
* Revert eip1559BaseFeeMinValue activation on xDai at London hardfork block
## OpenEthereum v3.3.0-rc.14
Enhancements:
* Add eip1559BaseFeeMinValue and eip1559BaseFeeMinValueTransition spec options
* Activate eip1559BaseFeeMinValue on xDai at London hardfork block (19040000), set it to 20 GWei
* Activate eip1559BaseFeeMinValue on POA Core at block 24199500 (November 8, 2021), set it to 10 GWei
* Delay difficulty bomb to June 2022 for Ethereum Mainnet (EIP-4345)
## OpenEthereum v3.3.0-rc.13
Enhancements:
* London hardfork block: POA Core (24090200)
## OpenEthereum v3.3.0-rc.12
Enhancements:
* London hardfork block: xDai (19040000)
## OpenEthereum v3.3.0-rc.11
Bug fixes:
* Ignore GetNodeData requests only for non-AuRa chains
## OpenEthereum v3.3.0-rc.10
Enhancements:
* Add eip1559FeeCollector and eip1559FeeCollectorTransition spec options
## OpenEthereum v3.3.0-rc.9
Bug fixes:
* Add service transactions support for EIP-1559
* Fix MinGasPrice config option for POSDAO and EIP-1559
Enhancements:
* min_gas_price becomes min_effective_priority_fee
* added version 4 for TxPermission contract
## OpenEthereum v3.3.0-rc.8
Bug fixes:
* Ignore GetNodeData requests (#519)
## OpenEthereum v3.3.0-rc.7
Bug fixes:
* GetPooledTransactions is sent in invalid form (wrong packet id)
## OpenEthereum v3.3.0-rc.6
Enhancements:
* London hardfork block: kovan (26741100) (#502)
## OpenEthereum v3.3.0-rc.4
Enhancements:
* London hardfork block: mainnet (12,965,000) (#475)
* Support for eth/66 protocol version (#465)
* Bump ethereum/tests to v9.0.3
* Add eth_feeHistory
Bug fixes:
* GetNodeData from eth63 is missing (#466)
* Effective gas price not omitting (#477)
* London support in openethereum-evm (#479)
* gasPrice is required field for Transaction object (#481)
## OpenEthereum v3.3.0-rc.3
Bug fixes:
* Add effective_gas_price to eth_getTransactionReceipt #445 (#450)
* Update eth_gasPrice to support EIP-1559 #449 (#458)
* eth_estimateGas returns "Requires higher than upper limit of X" after London Ropsten Hard Fork #459 (#460)
## OpenEthereum v3.3.0-rc.2
Enhancements:
* EIP-1559: Fee market change for ETH 1.0 chain
* EIP-3198: BASEFEE opcode
* EIP-3529: Reduction in gas refunds
* EIP-3541: Reject new contracts starting with the 0xEF byte
* Delay difficulty bomb to December 2021 (EIP-3554)
* London hardfork blocks: goerli (5,062,605), rinkeby (8,897,988), ropsten (10,499,401)
* Add chainspecs for aleut and baikal
* Bump ethereum/tests to v9.0.2
## OpenEthereum v3.2.6
Enhancement:
* Berlin hardfork blocks: poacore (21,364,900), poasokol (21,050,600)
## OpenEthereum v3.2.5
Bug fixes:
* Backport: Block sync stopped without any errors. #277 (#286)
* Strict memory order (#306)
Enhancements:
* Executable queue for ancient blocks inclusion (#208)
* Backport AuRa commits for xdai (#330)
* Add Nethermind to clients that accept service transactions (#324)
* Implement the filter argument in parity_pendingTransactions (#295)
* Ethereum-types and various libs upgraded (#315)
* [evmbin] Omit storage output, now for std-json (#311)
* Freeze pruning while creating snapshot (#205)
* AuRa multi block reward (#290)
* Improved metrics. DB read/write. prometheus prefix config (#240)
* Send RLPx auth in EIP-8 format (#287)
* rpc module reverted for RPC JSON api (#284)
* Revert "Remove eth/63 protocol version (#252)"
* Support for eth/65 protocol version (#366)
* Berlin hardfork blocks: kovan (24,770,900), xdai (16,101,500)
* Bump ethereum/tests to v8.0.3
devops:
* Upgrade docker alpine to `v1.13.2`. for rust `v1.47`.
* Send SIGTERM instead of SIGHUP to OE daemon (#317)
## OpenEthereum v3.2.4
* Fix for Typed transaction broadcast.
## OpenEthereum v3.2.3
* Hotfix for berlin consensus error.
## OpenEthereum v3.2.2-rc.1
Bug fixes:
* Backport: Block sync stopped without any errors. #277 (#286)
* Strict memory order (#306)
Enhancements:
* Executable queue for ancient blocks inclusion (#208)
* Backport AuRa commits for xdai (#330)
* Add Nethermind to clients that accept service transactions (#324)
* Implement the filter argument in parity_pendingTransactions (#295)
* Ethereum-types and various libs upgraded (#315)
* Bump ethereum/tests to v8.0.2
* [evmbin] Omit storage output, now for std-json (#311)
* Freeze pruning while creating snapshot (#205)
* AuRa multi block reward (#290)
* Improved metrics. DB read/write. prometheus prefix config (#240)
* Send RLPx auth in EIP-8 format (#287)
* rpc module reverted for RPC JSON api (#284)
* Revert "Remove eth/63 protocol version (#252)"
devops:
* Upgrade docker alpine to `v1.13.2`. for rust `v1.47`.
* Send SIGTERM instead of SIGHUP to OE daemon (#317)
## OpenEthereum v3.2.1
Hot fix issue, related to initial sync:
* Initial sync gets stuck. (#318)
## OpenEthereum v3.2.0
Bug fixes:
* Update EWF's chains with Istanbul transition block numbers (#11482) (#254)
* fix Supplied instant is later than self (#169)
* ethcore/snapshot: fix double-lock in Service::feed_chunk (#289)
Enhancements:
* Berlin hardfork blocks: mainnet (12,244,000), goerli (4,460,644), rinkeby (8,290,928) and ropsten (9,812,189)
* yolo3x spec (#241)
* EIP-2930 RPC support
* Remove eth/63 protocol version (#252)
* Snapshot manifest block added to prometheus (#232)
* EIP-1898: Allow default block parameter to be blockHash
* Change ProtocolId to U64
* Update ethereum/tests

4048
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +1,77 @@
[package]
description = "Parity Ethereum client"
name = "parity-ethereum"
description = "OpenEthereum"
name = "openethereum"
# NOTE Make sure to update util/version/Cargo.toml as well
version = "2.7.2"
version = "3.3.3"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
authors = [
"OpenEthereum developers",
"Parity Technologies <admin@parity.io>"
]
[dependencies]
ansi_term = "0.11"
atty = "0.2.8"
blooms-db = { path = "util/blooms-db" }
clap = "2"
cli-signer= { path = "cli-signer" }
client-traits = { path = "ethcore/client-traits" }
common-types = { path = "ethcore/types" }
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
dir = { path = "util/dir" }
docopt = "1.0"
engine = { path = "ethcore/engine" }
ethabi = { version = "9.0.1", optional = true }
ethcore = { path = "ethcore", features = ["parity"] }
ethcore-accounts = { path = "accounts", optional = true }
ethcore-blockchain = { path = "ethcore/blockchain" }
ethcore-call-contract = { path = "ethcore/call-contract", optional = true }
ethcore-db = { path = "ethcore/db" }
ethcore-io = { path = "util/io" }
ethcore-light = { path = "ethcore/light" }
ethcore-logger = { path = "parity/logger" }
ethcore-miner = { path = "miner" }
ethcore-network = { path = "util/network" }
ethcore-private-tx = { path = "ethcore/private-tx" }
ethcore-secretstore = { path = "secret-store", optional = true }
ethcore-service = { path = "ethcore/service" }
ethcore-sync = { path = "ethcore/sync" }
ethereum-types = "0.8.0"
ethkey = { path = "accounts/ethkey" }
ethstore = { path = "accounts/ethstore" }
fdlimit = "0.1"
futures = "0.1"
journaldb = { path = "util/journaldb" }
jsonrpc-core = "14.0.3"
keccak-hash = "0.4.0"
kvdb = "0.3.1"
kvdb-rocksdb = "0.4.1"
blooms-db = { path = "crates/db/blooms-db" }
log = "0.4"
migration-rocksdb = { path = "util/migration-rocksdb" }
node-filter = { path = "ethcore/node-filter" }
num_cpus = "1.2"
number_prefix = "0.2"
panic_hook = { path = "util/panic-hook" }
parity-bytes = "0.1"
parity-crypto = { version = "0.4.2", features = ["publickey"] }
parity-daemonize = "0.3"
parity-hash-fetch = { path = "updater/hash-fetch" }
parity-ipfs-api = { path = "ipfs" }
parity-local-store = { path = "miner/local-store" }
parity-path = "0.1"
parity-rpc = { path = "rpc" }
parity-runtime = { path = "util/runtime" }
parity-updater = { path = "updater" }
parity-util-mem = { version = "0.3.0", features = ["jemalloc-global"] }
parity-version = { path = "util/version" }
parking_lot = "0.9"
regex = "1.0"
registrar = { path = "util/registrar" }
rlp = "0.4.0"
rpassword = "1.0"
rustc-hex = "1.0"
semver = "0.9"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
snapshot = { path = "ethcore/snapshot" }
spec = { path = "ethcore/spec" }
docopt = "1.0"
clap = "2"
term_size = "0.3"
textwrap = "0.9"
num_cpus = "1.2"
number_prefix = "0.2"
rpassword = "1.0"
semver = "0.9"
ansi_term = "0.10"
parking_lot = "0.11.1"
regex = "1.0"
atty = "0.2.8"
toml = "0.4"
verification = { path = "ethcore/verification" }
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
futures = "0.1"
hyper = { version = "0.12" }
fdlimit = "0.1"
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
jsonrpc-core = "15.0.0"
parity-bytes = "0.1"
common-types = { path = "crates/ethcore/types" }
ethcore = { path = "crates/ethcore", features = ["parity"] }
ethcore-accounts = { path = "crates/accounts", optional = true }
ethcore-blockchain = { path = "crates/ethcore/blockchain" }
ethcore-call-contract = { path = "crates/vm/call-contract"}
ethcore-db = { path = "crates/db/db" }
ethcore-io = { path = "crates/runtime/io" }
ethcore-logger = { path = "bin/oe/logger" }
ethcore-miner = { path = "crates/concensus/miner" }
ethcore-network = { path = "crates/net/network" }
ethcore-service = { path = "crates/ethcore/service" }
ethcore-sync = { path = "crates/ethcore/sync" }
ethereum-types = "0.9.2"
ethkey = { path = "crates/accounts/ethkey" }
ethstore = { path = "crates/accounts/ethstore" }
fetch = { path = "crates/net/fetch" }
node-filter = { path = "crates/net/node-filter" }
parity-crypto = { version = "0.6.2", features = [ "publickey" ] }
rlp = { version = "0.4.6" }
cli-signer= { path = "crates/util/cli-signer" }
parity-daemonize = "0.3"
parity-local-store = { path = "crates/concensus/miner/local-store" }
parity-runtime = { path = "crates/runtime/runtime" }
parity-rpc = { path = "crates/rpc" }
parity-version = { path = "crates/util/version" }
parity-path = "0.1"
dir = { path = "crates/util/dir" }
panic_hook = { path = "crates/util/panic-hook" }
keccak-hash = "0.5.0"
migration-rocksdb = { path = "crates/db/migration-rocksdb" }
kvdb = "0.1"
kvdb-rocksdb = "0.1.3"
journaldb = { path = "crates/db/journaldb" }
stats = { path = "crates/util/stats" }
prometheus = "0.9.0"
# ethcore-secretstore = { path = "crates/util/secret-store", optional = true }
[build-dependencies]
rustc_version = "0.2"
@ -85,7 +80,8 @@ rustc_version = "0.2"
pretty_assertions = "0.1"
ipnetwork = "0.12.6"
tempdir = "0.3"
fake-fetch = { path = "util/fake-fetch" }
fake-fetch = { path = "crates/net/fake-fetch" }
lazy_static = "1.2.0"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
@ -95,11 +91,11 @@ default = ["accounts"]
accounts = ["ethcore-accounts", "parity-rpc/accounts"]
miner-debug = ["ethcore/miner-debug"]
json-tests = ["ethcore/json-tests"]
ci-skip-tests = ["ethcore/ci-skip-tests"]
test-heavy = ["ethcore/test-heavy"]
evm-debug = ["ethcore/evm-debug"]
evm-debug-tests = ["ethcore/evm-debug-tests"]
slow-blocks = ["ethcore/slow-blocks"]
secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"]
final = ["parity-version/final"]
deadlock_detection = ["parking_lot/deadlock_detection"]
# to create a memory profile (requires nightly rust), use e.g.
@ -109,17 +105,17 @@ deadlock_detection = ["parking_lot/deadlock_detection"]
# `valgrind --tool=massif /path/to/parity <parity params>`
# and `massif-visualizer` for visualization
memory_profiling = []
# hardcode version number 1.3.7 of parity to force an update
# in order to manually test that parity fall-over to the local version
# in case of invalid or deprecated command line arguments are entered
test-updater = ["parity-updater/test-updater"]
[lib]
path = "parity/lib.rs"
path = "bin/oe/lib.rs"
[[bin]]
path = "parity/main.rs"
name = "parity"
path = "bin/oe/main.rs"
name = "openethereum"
[profile.test]
lto = false
opt-level = 3 # makes tests slower to compile, but faster to run
[profile.release]
debug = false
@ -130,9 +126,8 @@ lto = true
# in the dependency tree in any other way
# (i.e. pretty much only standalone CLI tools)
members = [
"accounts/ethkey/cli",
"accounts/ethstore/cli",
"chainspec",
"ethcore/wasm/run",
"evmbin",
"bin/ethkey",
"bin/ethstore",
"bin/evmbin",
"bin/chainspec"
]

227
README.md
View File

@ -1,11 +1,19 @@
![Parity Ethereum](docs/logo-parity-ethereum.svg)
# OpenEthereum
<h2 align="center">The Fastest and most Advanced Ethereum Client.</h2>
Fast and feature-rich multi-network Ethereum client.
<p align="center"><strong><a href="https://github.com/paritytech/parity-ethereum/releases/latest">» Download the latest release «</a></strong></p>
[» Download the latest release «](https://github.com/openethereum/openethereum/releases/latest)
<p align="center"><a href="https://gitlab.parity.io/parity/parity-ethereum/commits/master" target="_blank"><img src="https://gitlab.parity.io/parity/parity-ethereum/badges/master/build.svg" /></a>
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"><img src="https://img.shields.io/badge/license-GPL%20v3-green.svg" /></a></p>
[![GPL licensed][license-badge]][license-url]
[![Build Status][ci-badge]][ci-url]
[![Discord chat][chat-badge]][chat-url]
[license-badge]: https://img.shields.io/badge/license-GPL_v3-green.svg
[license-url]: LICENSE
[ci-badge]: https://github.com/openethereum/openethereum/workflows/Build%20and%20Test%20Suite/badge.svg
[ci-url]: https://github.com/openethereum/openethereum/actions
[chat-badge]: https://img.shields.io/discord/669192218728202270.svg?logo=discord
[chat-url]: https://discord.io/openethereum
## Table of Contents
@ -14,19 +22,17 @@
3. [Building](#chapter-003)<br>
3.1 [Building Dependencies](#chapter-0031)<br>
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)
3.3 [Starting OpenEthereum](#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)
7. [Contributing](#chapter-008)
8. [License](#chapter-009)
## 1. Description <a id="chapter-001"></a>
**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. Parity Ethereum provides the core infrastructure essential for speedy and reliable services.
**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. OpenEthereum provides the core infrastructure essential for speedy and reliable services.
- Clean, modular codebase for easy customisation
- Advanced CLI-based client
@ -36,19 +42,19 @@
## 2. Technical Overview <a id="chapter-002"></a>
Parity Ethereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity Ethereum using the sophisticated and cutting-edge **Rust programming language**. Parity Ethereum is licensed under the GPLv3 and can be used for all your Ethereum needs.
OpenEthereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing OpenEthereum using the **Rust programming language**. OpenEthereum is licensed under the GPLv3 and can be used for all your Ethereum needs.
By default, Parity Ethereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs.
By default, OpenEthereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs.
If you run into problems while using Parity Ethereum, check out the [wiki for documentation](https://wiki.parity.io/), feel free to [file an issue in this repository](https://github.com/paritytech/parity-ethereum/issues/new), or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help! **For security-critical issues**, please refer to the security policy outlined in [SECURITY.md](SECURITY.md).
If you run into problems while using OpenEthereum, check out the [old wiki for documentation](https://openethereum.github.io/), feel free to [file an issue in this repository](https://github.com/openethereum/openethereum/issues/new), or hop on our [Discord](https://discord.io/openethereum) chat room to ask a question. We are glad to help!
Parity Ethereum's current beta-release is 2.6. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions.
You can download OpenEthereum's latest release at [the releases page](https://github.com/openethereum/openethereum/releases) or follow the instructions below to build from source. Read the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions.
## 3. Building <a id="chapter-003"></a>
### 3.1 Build Dependencies <a id="chapter-0031"></a>
Parity Ethereum requires **latest stable Rust version** to build.
OpenEthereum requires **latest stable Rust version** to build.
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have `rustup`, you can install it like this:
@ -57,7 +63,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
$ curl https://sh.rustup.rs -sSf | sh
```
Parity Ethereum also requires `gcc`, `g++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
OpenEthereum also requires `clang` (>= 9.0), `clang++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
- OSX:
```bash
@ -77,14 +83,14 @@ Once you have `rustup` installed, then you need to install:
* [Perl](https://www.perl.org)
* [Yasm](https://yasm.tortall.net)
Make sure that these binaries are in your `PATH`. After that, you should be able to build Parity Ethereum from source.
Make sure that these binaries are in your `PATH`. After that, you should be able to build OpenEthereum from source.
### 3.2 Build from Source Code <a id="chapter-0032"></a>
```bash
# download Parity Ethereum code
$ git clone https://github.com/paritytech/parity-ethereum
$ cd parity-ethereum
# download OpenEthereum code
$ git clone https://github.com/openethereum/openethereum
$ cd openethereum
# build in release mode
$ cargo build --release --features final
@ -104,50 +110,32 @@ Note, when compiling a crate and you receive errors, it's in most cases your out
$ cargo clean
```
This always compiles the latest nightly builds. If you want to build stable or beta, do a
This always compiles the latest nightly builds. If you want to build stable, do a
```bash
$ git checkout stable
```
or
```bash
$ git checkout beta
```
### 3.3 Simple One-Line Installer for Mac and Linux <a id="chapter-0033"></a>
```bash
bash <(curl https://get.parity.io -L)
```
The one-line installer always defaults to the latest beta release. To install a stable release, run:
```bash
bash <(curl https://get.parity.io -L) -r stable
```
### 3.4 Starting Parity Ethereum <a id="chapter-0034"></a>
### 3.3 Starting OpenEthereum <a id="chapter-0034"></a>
#### Manually
To start Parity Ethereum manually, just run
To start OpenEthereum manually, just run
```bash
$ ./target/release/parity
$ ./target/release/openethereum
```
so Parity Ethereum begins syncing the Ethereum blockchain.
so OpenEthereum begins syncing the Ethereum blockchain.
#### Using `systemd` service file
To start Parity Ethereum as a regular user using `systemd` init:
To start OpenEthereum as a regular user using `systemd` init:
1. Copy `./scripts/parity.service` to your
1. Copy `./scripts/openethereum.service` to your
`systemd` user directory (usually `~/.config/systemd/user`).
2. Copy release to bin folder, write `sudo install ./target/release/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.
2. Copy release to bin folder, write `sudo install ./target/release/openethereum /usr/bin/openethereum`
3. To configure OpenEthereum, see [our wiki](https://openethereum.github.io/Configuring-OpenEthereum) for details.
## 4. Testing <a id="chapter-004"></a>
@ -169,13 +157,11 @@ You can show your logs in the test output by passing `--nocapture` (i.e. `cargo
## 5. Documentation <a id="chapter-005"></a>
Official website: https://parity.io
Be sure to [check out our wiki](https://openethereum.github.io/) for more information.
Be sure to [check out our wiki](https://wiki.parity.io) for more information.
### Viewing documentation for OpenEthereum packages
### Viewing documentation for Parity Ethereum packages
You can generate documentation for Parity Ethereum Rust packages that automatically opens in your web browser using [rustdoc with Cargo](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html#using-rustdoc-with-cargo) (of the The Rustdoc Book), by running the the following commands:
You can generate documentation for OpenEthereum Rust packages that automatically opens in your web browser using [rustdoc with Cargo](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html#using-rustdoc-with-cargo) (of the The Rustdoc Book), by running the the following commands:
* **All** packages
```
@ -189,192 +175,137 @@ You can generate documentation for Parity Ethereum Rust packages that automatica
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`):
Replacing `<spec>` with one of the following from the details section below (i.e. `cargo doc --package openethereum --open`):
<a id="package-list"></a>
**Package List**
<details><p>
* Parity Ethereum (EthCore) Client Application
* OpenEthereum Client Application
```bash
parity-ethereum
openethereum
```
* Parity Ethereum Account Management, Key Management Tool, and Keys Generator
* OpenEthereum Account Management, Key Management Tool, and Keys Generator
```bash
ethcore-accounts, ethkey-cli, ethstore, ethstore-cli
```
* Parity Chain Specification
* OpenEthereum Chain Specification
```bash
chainspec
```
* Parity CLI Signer Tool & RPC Client
* OpenEthereum CLI Signer Tool & RPC Client
```bash
cli-signer parity-rpc-client
```
* Parity Ethereum Ethash & ProgPoW Implementations
* OpenEthereum Ethash & ProgPoW Implementations
```bash
ethash
```
* Parity (EthCore) Library
* EthCore Library
```bash
ethcore
```
* Parity Ethereum Blockchain Database, Test Generator, Configuration,
* OpenEthereum Blockchain Database, Test Generator, Configuration,
Caching, Importing Blocks, and Block Information
```bash
ethcore-blockchain
```
* Parity Ethereum (EthCore) Contract Calls and Blockchain Service & Registry Information
* OpenEthereum Contract Calls and Blockchain Service & Registry Information
```bash
ethcore-call-contract
```
* Parity Ethereum (EthCore) Database Access & Utilities, Database Cache Manager
* OpenEthereum Database Access & Utilities, Database Cache Manager
```bash
ethcore-db
```
* Parity Ethereum Virtual Machine (EVM) Rust Implementation
* OpenEthereum Virtual Machine (EVM) Rust Implementation
```bash
evm
```
* Parity Ethereum (EthCore) Light Client Implementation
* OpenEthereum Light Client Implementation
```bash
ethcore-light
```
* Parity Smart Contract based Node Filter, Manage Permissions of Network Connections
* Smart Contract based Node Filter, Manage Permissions of Network Connections
```bash
node-filter
```
* Parity Private Transactions
```bash
ethcore-private-tx
```
* Parity Ethereum (EthCore) Client & Network Service Creation & Registration with the I/O Subsystem
* OpenEthereum Client & Network Service Creation & Registration with the I/O Subsystem
```bash
ethcore-service
```
* Parity Ethereum (EthCore) Blockchain Synchronization
* OpenEthereum Blockchain Synchronization
```bash
ethcore-sync
```
* Parity Ethereum Common Types
* OpenEthereum Common Types
```bash
common-types
```
* Parity Ethereum Virtual Machines (VM) Support Library
* OpenEthereum Virtual Machines (VM) Support Library
```bash
vm
```
* Parity Ethereum WASM Interpreter
* OpenEthereum WASM Interpreter
```bash
wasm
```
* Parity Ethereum WASM Test Runner
* OpenEthereum WASM Test Runner
```bash
pwasm-run-test
```
* Parity EVM Implementation
* OpenEthereum EVM Implementation
```bash
evmbin
```
* Parity Ethereum IPFS-compatible API
```bash
parity-ipfs-api
```
* Parity Ethereum JSON Deserialization
* OpenEthereum JSON Deserialization
```bash
ethjson
```
* Parity Ethereum State Machine Generalization for Consensus Engines
* OpenEthereum State Machine Generalization for Consensus Engines
```bash
parity-machine
```
* Parity Ethereum (EthCore) Miner Interface
* OpenEthereum Miner Interface
```bash
ethcore-miner parity-local-store price-info ethcore-stratum using_queue
```
* Parity Ethereum (EthCore) Logger Implementation
* OpenEthereum Logger Implementation
```bash
ethcore-logger
```
* C bindings library for the Parity Ethereum client
```bash
parity-clib
```
* Parity Ethereum JSON-RPC Servers
* OpenEthereum JSON-RPC Servers
```bash
parity-rpc
```
* Parity Ethereum (EthCore) Secret Store
```bash
ethcore-secretstore
```
* Parity Updater Service
* OpenEthereum Updater Service
```bash
parity-updater parity-hash-fetch
```
* Parity Core Libraries (Parity Util)
* OpenEthereum Core Libraries (`util`)
```bash
ethcore-bloom-journal blooms-db dir eip-712 fake-fetch fastmap fetch ethcore-io
journaldb keccak-hasher len-caching-lock macros memory-cache memzero
accounts-bloom blooms-db dir eip-712 fake-fetch fastmap fetch ethcore-io
journaldb keccak-hasher len-caching-lock memory-cache memzero
migration-rocksdb ethcore-network ethcore-network-devp2p panic_hook
patricia-trie-ethereum registrar rlp_compress rlp_derive parity-runtime stats
patricia-trie-ethereum registrar rlp_compress stats
time-utils triehash-ethereum unexpected parity-version
```
</p></details>
### Contributing to documentation for Parity Ethereum packages
[Document source code](https://doc.rust-lang.org/1.9.0/book/documentation.html) for Parity Ethereum packages by annotating the source code with documentation comments.
Example (generic documentation comment):
```markdown
/// Summary
///
/// Description
///
/// # Panics
///
/// # Errors
///
/// # Safety
///
/// # Examples
///
/// Summary of Example 1
///
/// ```rust
/// // insert example 1 code here for use with documentation as tests
/// ```
///
```
## 6. Toolchain <a id="chapter-006"></a>
In addition to the Parity Ethereum client, there are additional tools in this repository available:
In addition to the OpenEthereum client, there are additional tools in this repository available:
- [evmbin](./evmbin) - Parity Ethereum EVM Implementation.
- [ethstore](./accounts/ethstore) - Parity Ethereum Key Management.
- [ethkey](./accounts/ethkey) - Parity Ethereum Keys Generator.
- [evmbin](./bin/evmbin) - OpenEthereum EVM Implementation.
- [ethstore](./crates/accounts/ethstore) - OpenEthereum Key Management.
- [ethkey](./crates/accounts/ethkey) - OpenEthereum Keys Generator.
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)
- [whisper](https://github.com/paritytech/whisper) - Parity Ethereum Whisper-v2 PoC Implementation.
The following tools are available in a separate repository:
- [ethabi](https://github.com/openethereum/ethabi) - OpenEthereum Encoding of Function Calls. [Docs here](https://crates.io/crates/ethabi)
- [whisper](https://github.com/openethereum/whisper) - OpenEthereum Whisper-v2 PoC Implementation.
## 7. Community <a id="chapter-007"></a>
### Join the chat!
Questions? Get in touch with us on Gitter:
[![Gitter: Parity](https://img.shields.io/badge/gitter-parity-4AB495.svg)](https://gitter.im/paritytech/parity)
[![Gitter: Parity.js](https://img.shields.io/badge/gitter-parity.js-4AB495.svg)](https://gitter.im/paritytech/parity.js)
[![Gitter: Parity/Miners](https://img.shields.io/badge/gitter-parity/miners-4AB495.svg)](https://gitter.im/paritytech/parity/miners)
[![Gitter: Parity-PoA](https://img.shields.io/badge/gitter-parity--poa-4AB495.svg)](https://gitter.im/paritytech/parity-poa)
Alternatively, join our community on Matrix:
[![Riot: +Parity](https://img.shields.io/badge/riot-%2Bparity%3Amatrix.parity.io-orange.svg)](https://riot.im/app/#/group/+parity:matrix.parity.io)
## 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).
@ -382,6 +313,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)

View File

@ -1,101 +0,0 @@
# Security Policy
Parity Technologies is committed to resolving security vulnerabilities in our software quickly and carefully. We take the necessary steps to minimize risk, provide timely information, and deliver vulnerability fixes and mitigations required to address security issues.
## Reporting a Vulnerability
Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io.
Your report should include the following:
- your name
- description of the vulnerability
- attack scenario (if any)
- components
- reproduction
- other details
Try to include as much information in your report as you can, including a description of the vulnerability, its potential impact, and steps for reproducing it. Be sure to use a descriptive subject line.
You'll receive a response to your email within two business days indicating the next steps in handling your report. We encourage finders to use encrypted communication channels to protect the confidentiality of vulnerability reports. You can encrypt your report using our public key. This key is [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73) server and reproduced below.
After the initial reply to your report, our team will endeavor to keep you informed of the progress being made towards a fix. These updates will be sent at least every five business days.
Thank you for taking the time to responsibly disclose any vulnerabilities you find.
## Responsible Investigation and Reporting
Responsible investigation and reporting includes, but isn't limited to, the following:
- Don't violate the privacy of other users, destroy data, etc.
- Dont defraud or harm Parity Technologies Ltd or its users during your research; you should make a good faith effort to not interrupt or degrade our services.
- Don't target our physical security measures, or attempt to use social engineering, spam, distributed denial of service (DDOS) attacks, etc.
- Initially report the bug only to us and not to anyone else.
- Give us a reasonable amount of time to fix the bug before disclosing it to anyone else, and give us adequate written warning before disclosing it to anyone else.
- In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us or our users. Otherwise your actions might be interpreted as an attack rather than an effort to be helpful.
## Bug Bounty Program
Our Bug Bounty Program allows us to recognise and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html).
## Plaintext PGP Key
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBF0vHwQBEADKui4qAo4bzdzRhMm+uhUpYGf8jjjmET3zJ8kKQIpp6JTsV+HJ
6m1We0QYeMRXoOYH1xVHBf2zNCuHS0nSQdUCQA7SHWsPB05STa2hvlR7fSdQnCCp
gnLOJWXvvedlRDIAhvqI6cwLdUlXgVSKEwrwmrpiBhh4NxI3qX+LyIa+Ovkchu2S
d/YCnE4GqojSGRfJYiGwe2N+sF7OfaoKhQuTrtdDExHrMU4cWnTXW2wyxTr4xkj9
jS2WeLVZWflvkDHT8JD9N6jNxBVEF/Qvjk83zI0kCOzkhek8x+YUgfLq3/rHOYbX
3pW21ccHYPacHjHWvKE+xRebjeEhJ4KxKHfCVjQcxybwDBqDka1AniZt4CQ7UORf
MU/ue2oSZ9nNg0uMdb/0AbQPZ04OlMcYPAPWzFL08nVPox9wT9uqlL6JtcOeC90h
oOeDmfgwmjMmdwWTRgt9qQjcbgXzVvuAzIGbzj1X3MdLspWdHs/d2+US4nji1TkN
oYIW7vE+xkd3aB+NZunIlm9Rwd/0mSgDg+DaNa5KceOLhq0/qKgcXC/RRU29I8II
tusRoR/oesGJGYTjh4k6PJkG+nvDPsoQrwYT44bhnniS1xYkxWYXF99JFI7LgMdD
e1SgKeIDVpvm873k82E6arp5655Wod1XOjaXBggCwFp84eKcEZEN+1qEWwARAQAB
tClQYXJpdHkgU2VjdXJpdHkgVGVhbSA8c2VjdXJpdHlAcGFyaXR5LmlvPokCVAQT
AQoAPhYhBJ1LK264+XFW0ZZpqf8IEtSRuWeYBQJdLx8EAhsDBQkDwmcABQsJCAcC
BhUKCQgLAgQWAgMBAh4BAheAAAoJEP8IEtSRuWeYL84QAI6NwnwS561DWYYRAd4y
ocGPr3CnwFSt1GjkSkRy3B+tMhzexBg1y7EbLRUefIrO4LwOlywtRk8tTRGgEI4i
5xRLHbOkeolfgCFSpOj5d8cMKCt5HEIv18hsv6dkrzlSYA5NLX/GRBEh3F/0sGny
vCXapfxa1cx72sU7631JBK7t2Tf+MfwxdfyFZ9TI9WdtP5AfVjgTkIVkEDFcZPTc
n3CYXqTYFIBCNUD8LP4iTi3xUt7pTGJQQoFT8l15nJCgzRYQ+tXpoTRlf+/LtXmw
6iidPV87E06jHdK9666rBouIabAtx7i0/4kwo+bSZ8DiSKRUaehiHGd212HSEmdF
jxquWE4pEzoUowYznhSIfR+WWIqRBHxEYarP4m98Hi+VXZ7Fw1ytzO8+BAKnLXnj
2W2+T9qJks5gqVEoaWNnqpvya6JA11QZvZ0w7Om2carDc2ILNm2Xx9J0mRUye8P0
KxcgqJuKNGFtugebQAsXagkxOKsdKna1PlDlxEfTf6AgI3ST8qSiMAwaaIMB/REF
VKUapGoslQX4tOCjibI2pzEgE//D8NAaSVu2A9+BUcFERdZRxsI7fydIXNeZ2R46
N2qfW+DP3YR/14QgdRxDItEavUoE1vByRXwIufKAkVemOZzIoFXKFsDeXwqTVW5i
6CXu6OddZ3QHDiT9TEbRny4QuQINBF0vKCwBEACnP5J7LEGbpxNBrPvGdxZUo0YA
U8RgeKDRPxJTvMo27V1IPZGaKRCRq8LBfg/eHhqZhQ7SLJBjBljd8kuT5dHDBTRe
jE1UIOhmnlSlrEJjAmpVO08irlGpq1o+8mGcvkBsR0poCVjeNeSnwYfRnR+c3GK5
Er6/JRqfN4mJvnEC9/Pbm6C7ql6YLKxC3yqzF97JL5brbbuozrW7nixY/yAI8619
VlBIMP7PAUbGcnSQyuV5b/Wr2Sgr6NJclnNSLjh2U9/Du6w/0tDGlMBts8HjRnWJ
BXbkTdQKCTaqgK68kTKSiN1/x+lynxHC2AavMpH/08Kopg2ZCzJowMKIgcB+4Z/I
DJKZWHWKumhaZMGXcWgzgcByog9IpamuROEZFJNEUAFf7YIncEckPSif4looiOdS
VurKZGvYXXaGSsZbGgHxI5CWu7ZxMdLBLvtOcCYmRQrG+g/h+PGU5BT0bNAfNTkm
V3/n1B/TWbpWRmB3AwT2emQivXHkaubGI0VivhaO43AuI9JWoqiMqFtxbuTeoxwD
xlu2Dzcp0v+AR4T5cIG9D5/+yiPc25aIY7cIKxuNFHIDL4td5fwSGC7vU6998PIG
2Y48TGBnw7zpEfDfMayqAeBjX0YU6PTNsvS5O6bP3j4ojTOUYD7Z8QdCvgISDID3
WMGAdmSwmCRvsQ/OJwARAQABiQI8BBgBCgAmFiEEnUsrbrj5cVbRlmmp/wgS1JG5
Z5gFAl0vKCwCGwwFCQB2pwAACgkQ/wgS1JG5Z5hdbw//ZqR+JcWm59NUIHjauETJ
sYDYhcAfa3txTacRn5uPz/TQiTd7wZ82+G8Et0ZnpEHy6eWyBqHpG0hiPhFBzxjY
nhjHl8jJeyo2mQIVJhzkL58BHBZk8WM2TlaU7VxZ6TYOmP2y3qf6FD6mCcrQ4Fml
E9f0lyVUoI/5Zs9oF0izRk8vkwaY3UvLM7XEY6nM8GnFG8kaiZMYmx26Zo7Uz31G
7EGGZFsrVDXfNhSJyz79Gyn+Lx9jOTdoR0sH/THYIIosE83awMGE6jKeuDYTbVWu
+ZtHQef+pRteki3wvNLJK+kC1y3BtHqDJS9Lqx0s8SCiVozlC+fZfC9hCtU7bXJK
0UJZ4qjSvj6whzfaNgOZAqJpmwgOnd8W/3YJk1DwUeX98FcU38MR23SOkx2EDdDE
77Kdu62vTs/tLmOTuyKBvYPaHaYulYjQTxurG+o8vhHtaL87ARvuq+83dj+nO5z3
5O9vkcVJYWjOEnJe7ZvCTxeLJehpCmHIbyUuDx5P24MWVbyXOxIlxNxTqlub5GlW
rQF6Qsa/0k9TRk7Htbct6fAA0/VahJS0g096MrTH8AxBXDNE8lIoNeGikVlaxK9Z
S+aannlWYIJymZ4FygIPPaRlzhAoXBuJd8OaR5giC7dS1xquxKOiQEXTGsLeGFaI
BZYiIhW7GG4ozvKDqyNm4eg=
=yKcB
-----END PGP PUBLIC KEY BLOCK-----
```

View File

@ -1,13 +0,0 @@
[package]
description = "Parity Ethereum Keys Generator"
name = "ethkey"
version = "0.4.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
edit-distance = "2.0"
log = "0.4"
serde = "1.0"
serde_derive = "1.0"
parity-crypto = { version = "0.4.2", features = ["publickey"] }
parity-wordlist = "1.3"

View File

@ -1,453 +0,0 @@
// Copyright 2015-2020 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/>.
extern crate docopt;
extern crate env_logger;
extern crate ethkey;
extern crate panic_hook;
extern crate parity_wordlist;
extern crate parity_crypto;
extern crate rustc_hex;
extern crate serde;
extern crate threadpool;
#[macro_use]
extern crate serde_derive;
use std::num::ParseIntError;
use std::{env, fmt, process, io, sync};
use docopt::Docopt;
use ethkey::{Brain, BrainPrefix, Prefix, brain_recover};
use parity_crypto::publickey::{KeyPair, Random, Error as EthkeyError, Generator, sign, verify_public, verify_address};
use rustc_hex::{FromHex, FromHexError};
const USAGE: &'static str = r#"
Parity Ethereum keys generator.
Copyright 2015-2020 Parity Technologies (UK) Ltd.
Usage:
ethkey info <secret-or-phrase> [options]
ethkey generate random [options]
ethkey generate prefix <prefix> [options]
ethkey sign <secret> <message>
ethkey verify public <public> <signature> <message>
ethkey verify address <address> <signature> <message>
ethkey recover <address> <known-phrase>
ethkey [-h | --help]
Options:
-h, --help Display this message and exit.
-s, --secret Display only the secret key.
-p, --public Display only the public key.
-a, --address Display only the address.
-b, --brain Use parity brain wallet algorithm. Not recommended.
Commands:
info Display public key and address of the secret.
generate random Generates new random Ethereum key.
generate prefix Random generation, but address must start with a prefix ("vanity address").
sign Sign message using a secret key.
verify Verify signer of the signature by public key or address.
recover Try to find brain phrase matching given address from partial phrase.
"#;
#[derive(Debug, Deserialize)]
struct Args {
cmd_info: bool,
cmd_generate: bool,
cmd_random: bool,
cmd_prefix: bool,
cmd_sign: bool,
cmd_verify: bool,
cmd_public: bool,
cmd_address: bool,
cmd_recover: bool,
arg_prefix: String,
arg_secret: String,
arg_secret_or_phrase: String,
arg_known_phrase: String,
arg_message: String,
arg_public: String,
arg_address: String,
arg_signature: String,
flag_secret: bool,
flag_public: bool,
flag_address: bool,
flag_brain: bool,
}
#[derive(Debug)]
enum Error {
Ethkey(EthkeyError),
FromHex(FromHexError),
ParseInt(ParseIntError),
Docopt(docopt::Error),
Io(io::Error),
}
impl From<EthkeyError> for Error {
fn from(err: EthkeyError) -> Self {
Error::Ethkey(err)
}
}
impl From<FromHexError> for Error {
fn from(err: FromHexError) -> Self {
Error::FromHex(err)
}
}
impl From<ParseIntError> for Error {
fn from(err: ParseIntError) -> Self {
Error::ParseInt(err)
}
}
impl From<docopt::Error> for Error {
fn from(err: docopt::Error) -> Self {
Error::Docopt(err)
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Error::Ethkey(ref e) => write!(f, "{}", e),
Error::FromHex(ref e) => write!(f, "{}", e),
Error::ParseInt(ref e) => write!(f, "{}", e),
Error::Docopt(ref e) => write!(f, "{}", e),
Error::Io(ref e) => write!(f, "{}", e),
}
}
}
enum DisplayMode {
KeyPair,
Secret,
Public,
Address,
}
impl DisplayMode {
fn new(args: &Args) -> Self {
if args.flag_secret {
DisplayMode::Secret
} else if args.flag_public {
DisplayMode::Public
} else if args.flag_address {
DisplayMode::Address
} else {
DisplayMode::KeyPair
}
}
}
fn main() {
panic_hook::set_abort();
env_logger::try_init().expect("Logger initialized only once.");
match execute(env::args()) {
Ok(ok) => println!("{}", ok),
Err(Error::Docopt(ref e)) => e.exit(),
Err(err) => {
eprintln!("{}", err);
process::exit(1);
}
}
}
fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
let keypair = result.0;
match mode {
DisplayMode::KeyPair => match result.1 {
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
None => format!("{}", keypair)
},
DisplayMode::Secret => format!("{:x}", keypair.secret()),
DisplayMode::Public => format!("{:x}", keypair.public()),
DisplayMode::Address => format!("{:x}", keypair.address()),
}
}
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).deserialize())?;
return if args.cmd_info {
let display_mode = DisplayMode::new(&args);
let result = if args.flag_brain {
let phrase = args.arg_secret_or_phrase;
let phrase_info = validate_phrase(&phrase);
let keypair = Brain::new(phrase).generate().expect("Brain wallet generator is infallible; qed");
(keypair, Some(phrase_info))
} else {
let secret = args.arg_secret_or_phrase.parse().map_err(|_| EthkeyError::InvalidSecretKey)?;
(KeyPair::from_secret(secret)?, None)
};
Ok(display(result, display_mode))
} else if args.cmd_generate {
let display_mode = DisplayMode::new(&args);
let result = if args.cmd_random {
if args.flag_brain {
let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS);
let keypair = brain.generate()?;
let phrase = format!("recovery phrase: {}", brain.phrase());
(keypair, Some(phrase))
} else {
(Random.generate()?, None)
}
} else if args.cmd_prefix {
let prefix = args.arg_prefix.from_hex()?;
let brain = args.flag_brain;
in_threads(move || {
let iterations = 1024;
let prefix = prefix.clone();
move || {
let prefix = prefix.clone();
let res = if brain {
let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS);
let result = brain.generate();
let phrase = format!("recovery phrase: {}", brain.phrase());
result.map(|keypair| (keypair, Some(phrase)))
} else {
let result = Prefix::new(prefix, iterations).generate();
result.map(|res| (res, None))
};
Ok(res.map(Some).unwrap_or(None))
}
})?
} else {
return Ok(format!("{}", USAGE))
};
Ok(display(result, display_mode))
} else if args.cmd_sign {
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecretKey)?;
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
let signature = sign(&secret, &message)?;
Ok(format!("{}", signature))
} else if args.cmd_verify {
let signature = args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature)?;
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
let ok = if args.cmd_public {
let public = args.arg_public.parse().map_err(|_| EthkeyError::InvalidPublicKey)?;
verify_public(&public, &signature, &message)?
} else if args.cmd_address {
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
verify_address(&address, &signature, &message)?
} else {
return Ok(format!("{}", USAGE))
};
Ok(format!("{}", ok))
} else if args.cmd_recover {
let display_mode = DisplayMode::new(&args);
let known_phrase = args.arg_known_phrase;
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
let (phrase, keypair) = in_threads(move || {
let mut it = brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS);
move || {
let mut i = 0;
while let Some(phrase) = it.next() {
i += 1;
let keypair = Brain::new(phrase.clone()).generate().unwrap();
if keypair.address() == address {
return Ok(Some((phrase, keypair)))
}
if i >= 1024 {
return Ok(None)
}
}
Err(EthkeyError::Custom("Couldn't find any results.".into()))
}
})?;
Ok(display((keypair, Some(phrase)), display_mode))
} else {
Ok(format!("{}", USAGE))
}
}
const BRAIN_WORDS: usize = 12;
fn validate_phrase(phrase: &str) -> String {
match Brain::validate_phrase(phrase, BRAIN_WORDS) {
Ok(()) => format!("The recovery phrase looks correct.\n"),
Err(err) => format!("The recover phrase was not generated by Parity: {}", err)
}
}
fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError> where
O: Send + 'static,
X: Send + 'static,
F: Fn() -> X,
X: FnMut() -> Result<Option<O>, EthkeyError>,
{
let pool = threadpool::Builder::new().build();
let (tx, rx) = sync::mpsc::sync_channel(1);
let is_done = sync::Arc::new(sync::atomic::AtomicBool::default());
for _ in 0..pool.max_count() {
let is_done = is_done.clone();
let tx = tx.clone();
let mut task = prepare();
pool.execute(move || {
loop {
if is_done.load(sync::atomic::Ordering::SeqCst) {
return;
}
let res = match task() {
Ok(None) => continue,
Ok(Some(v)) => Ok(v),
Err(err) => Err(err),
};
// We are interested only in the first response.
let _ = tx.send(res);
}
});
}
if let Ok(solution) = rx.recv() {
is_done.store(true, sync::atomic::Ordering::SeqCst);
return solution;
}
Err(EthkeyError::Custom("No results found.".into()))
}
#[cfg(test)]
mod tests {
use super::execute;
#[test]
fn info() {
let command = vec!["ethkey", "info", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected =
"secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn brain() {
let command = vec!["ethkey", "info", "--brain", "this is sparta"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected =
"The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn secret() {
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn public() {
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn address() {
let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn sign() {
let command = vec!["ethkey", "sign", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_valid_public() {
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "true".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_valid_address() {
let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "true".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_invalid() {
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "false".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
}

View File

@ -1,75 +0,0 @@
// Copyright 2015-2020 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/>.
use std::convert::Infallible;
use parity_crypto::publickey::{KeyPair, Generator, Secret};
use parity_crypto::Keccak256;
use parity_wordlist;
/// Simple brainwallet.
pub struct Brain(String);
impl Brain {
pub fn new(s: String) -> Self {
Brain(s)
}
pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> {
parity_wordlist::validate_phrase(phrase, expected_words)
}
}
impl Generator for Brain {
type Error = Infallible;
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
let seed = self.0.clone();
let mut secret = seed.into_bytes().keccak256();
let mut i = 0;
loop {
secret = secret.keccak256();
match i > 16384 {
false => i += 1,
true => {
if let Ok(pair) = Secret::import_key(&secret)
.and_then(KeyPair::from_secret)
{
if pair.address()[0] == 0 {
trace!("Testing: {}, got: {:?}", self.0, pair.address());
return Ok(pair)
}
}
},
}
}
}
}
#[cfg(test)]
mod tests {
use Brain;
use parity_crypto::publickey::Generator;
#[test]
fn test_brain() {
let words = "this is sparta!".to_owned();
let first_keypair = Brain::new(words.clone()).generate().unwrap();
let second_keypair = Brain::new(words.clone()).generate().unwrap();
assert_eq!(first_keypair.secret(), second_keypair.secret());
}
}

View File

@ -1,72 +0,0 @@
// Copyright 2015-2020 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/>.
use super::Brain;
use parity_crypto::publickey::{Generator, KeyPair, Error};
use parity_wordlist as wordlist;
/// Tries to find brain-seed keypair with address starting with given prefix.
pub struct BrainPrefix {
prefix: Vec<u8>,
iterations: usize,
no_of_words: usize,
last_phrase: String,
}
impl BrainPrefix {
pub fn new(prefix: Vec<u8>, iterations: usize, no_of_words: usize) -> Self {
BrainPrefix {
prefix,
iterations,
no_of_words,
last_phrase: String::new(),
}
}
pub fn phrase(&self) -> &str {
&self.last_phrase
}
}
impl Generator for BrainPrefix {
type Error = Error;
fn generate(&mut self) -> Result<KeyPair, Error> {
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) {
self.last_phrase = phrase;
return Ok(keypair)
}
}
Err(Error::Custom("Could not find keypair".into()))
}
}
#[cfg(test)]
mod tests {
use BrainPrefix;
use parity_crypto::publickey::Generator;
#[test]
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));
}
}

View File

@ -1,174 +0,0 @@
// Copyright 2015-2020 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/>.
use std::collections::HashSet;
use edit_distance::edit_distance;
use parity_wordlist;
use super::Brain;
use parity_crypto::publickey::{Address, Generator};
/// Tries to find a phrase for address, given the number
/// of expected words and a partial phrase.
///
/// Returns `None` if phrase couldn't be found.
pub fn brain_recover(
address: &Address,
known_phrase: &str,
expected_words: usize,
) -> Option<String> {
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
for phrase in it {
let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed");
trace!("Testing: {}, got: {:?}", phrase, keypair.address());
if &keypair.address() == address {
return Some(phrase);
}
}
None
}
fn generate_substitutions(word: &str) -> Vec<&'static str> {
let mut words = parity_wordlist::WORDS.iter().cloned()
.map(|w| (edit_distance(w, word), w))
.collect::<Vec<_>>();
words.sort_by(|a, b| a.0.cmp(&b.0));
words.into_iter()
.map(|pair| pair.1)
.collect()
}
/// Iterator over possible
pub struct PhrasesIterator {
words: Vec<Vec<&'static str>>,
combinations: u64,
indexes: Vec<usize>,
has_next: bool,
}
impl PhrasesIterator {
pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self {
let known_words = parity_wordlist::WORDS.iter().cloned().collect::<HashSet<_>>();
let mut words = known_phrase.split(' ')
.map(|word| match known_words.get(word) {
None => {
info!("Invalid word '{}', looking for potential substitutions.", word);
let substitutions = generate_substitutions(word);
info!("Closest words: {:?}", &substitutions[..10]);
substitutions
},
Some(word) => vec![*word],
})
.collect::<Vec<_>>();
// add missing words
if words.len() < expected_words {
let to_add = expected_words - words.len();
info!("Number of words is insuficcient adding {} more.", to_add);
for _ in 0..to_add {
words.push(parity_wordlist::WORDS.iter().cloned().collect());
}
}
// start searching
PhrasesIterator::new(words)
}
pub fn new(words: Vec<Vec<&'static str>>) -> Self {
let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64);
let indexes = words.iter().map(|_| 0).collect();
info!("Starting to test {} possible combinations.", combinations);
PhrasesIterator {
words,
combinations,
indexes,
has_next: combinations > 0,
}
}
pub fn combinations(&self) -> u64 {
self.combinations
}
fn current(&self) -> String {
let mut s = self.words[0][self.indexes[0]].to_owned();
for i in 1..self.indexes.len() {
s.push(' ');
s.push_str(self.words[i][self.indexes[i]]);
}
s
}
fn next_index(&mut self) -> bool {
let mut pos = self.indexes.len();
while pos > 0 {
pos -= 1;
self.indexes[pos] += 1;
if self.indexes[pos] >= self.words[pos].len() {
self.indexes[pos] = 0;
} else {
return true;
}
}
false
}
}
impl Iterator for PhrasesIterator {
type Item = String;
fn next(&mut self) -> Option<String> {
if !self.has_next {
return None;
}
let phrase = self.current();
self.has_next = self.next_index();
Some(phrase)
}
}
#[cfg(test)]
mod tests {
use super::PhrasesIterator;
#[test]
fn should_generate_possible_combinations() {
let mut it = PhrasesIterator::new(vec![
vec!["1", "2", "3"],
vec!["test"],
vec!["a", "b", "c"],
]);
assert_eq!(it.combinations(), 9);
assert_eq!(it.next(), Some("1 test a".to_owned()));
assert_eq!(it.next(), Some("1 test b".to_owned()));
assert_eq!(it.next(), Some("1 test c".to_owned()));
assert_eq!(it.next(), Some("2 test a".to_owned()));
assert_eq!(it.next(), Some("2 test b".to_owned()));
assert_eq!(it.next(), Some("2 test c".to_owned()));
assert_eq!(it.next(), Some("3 test a".to_owned()));
assert_eq!(it.next(), Some("3 test b".to_owned()));
assert_eq!(it.next(), Some("3 test c".to_owned()));
assert_eq!(it.next(), None);
}
}

View File

@ -1,60 +0,0 @@
// Copyright 2015-2020 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/>.
use parity_crypto::publickey::{Random, Generator, KeyPair, Error};
/// Tries to find keypair with address starting with given prefix.
pub struct Prefix {
prefix: Vec<u8>,
iterations: usize,
}
impl Prefix {
pub fn new(prefix: Vec<u8>, iterations: usize) -> Self {
Prefix {
prefix: prefix,
iterations: iterations,
}
}
}
impl Generator for Prefix {
type Error = Error;
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) {
return Ok(keypair)
}
}
Err(Error::Custom("Could not find keypair".into()))
}
}
#[cfg(test)]
mod tests {
use Prefix;
use parity_crypto::publickey::Generator;
#[test]
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));
}
}

View File

@ -1,67 +0,0 @@
// Copyright 2015-2020 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/>.
use std::{cmp, thread};
use std::sync::Arc;
use std::collections::VecDeque;
use parking_lot::Mutex;
use ethstore::{PresaleWallet, Error};
use ethkey::Password;
use num_cpus;
pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> {
let passwords = Arc::new(Mutex::new(passwords));
let mut handles = Vec::new();
for _ in 0..num_cpus::get() {
let passwords = passwords.clone();
let wallet = PresaleWallet::open(&wallet_path)?;
handles.push(thread::spawn(move || {
look_for_password(passwords, wallet);
}));
}
for handle in handles {
handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
}
Ok(())
}
fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleWallet) {
let mut counter = 0;
while !passwords.lock().is_empty() {
let package = {
let mut passwords = passwords.lock();
let len = passwords.len();
passwords.split_off(cmp::min(len, 32))
};
for pass in package {
counter += 1;
match wallet.decrypt(&pass) {
Ok(_) => {
println!("Found password: {}", pass.as_str());
passwords.lock().clear();
return;
},
_ if counter % 100 == 0 => print!("."),
_ => {},
}
}
}
}

View File

@ -1,320 +0,0 @@
// Copyright 2015-2020 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/>.
extern crate dir;
extern crate docopt;
extern crate ethstore;
extern crate ethkey;
extern crate num_cpus;
extern crate panic_hook;
extern crate parking_lot;
extern crate parity_crypto;
extern crate rustc_hex;
extern crate serde;
extern crate env_logger;
#[macro_use]
extern crate serde_derive;
use std::collections::VecDeque;
use std::io::Read;
use std::{env, process, fs, fmt};
use docopt::Docopt;
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
use ethkey::Password;
use parity_crypto::publickey::Address;
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
mod crack;
pub const USAGE: &'static str = r#"
Parity Ethereum key management tool.
Copyright 2015-2020 Parity Technologies (UK) Ltd.
Usage:
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore import [<password>] [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore find-wallet-pass <path> <password>
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore list-vaults [--dir DIR]
ethstore create-vault <vault> <password> [--dir DIR]
ethstore change-vault-pwd <vault> <old-pwd> <new-pwd> [--dir DIR]
ethstore move-to-vault <address> <vault> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore move-from-vault <address> <vault> <password> [--dir DIR]
ethstore [-h | --help]
Options:
-h, --help Display this message and exit.
--dir DIR Specify the secret store directory. It may be either
parity, parity-(chain), geth, geth-test
or a path [default: parity].
--vault VAULT Specify vault to use in this operation.
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
that this option is required when vault option is set.
Otherwise it is ignored.
--src DIR Specify import source. It may be either
parity, parity-(chain), geth, geth-test
or a path [default: geth].
Commands:
insert Save account with password.
change-pwd Change password.
list List accounts.
import Import accounts from src.
import-wallet Import presale wallet.
find-wallet-pass Tries to open a wallet with list of passwords given.
remove Remove account.
sign Sign message.
public Displays public key for an address.
list-vaults List vaults.
create-vault Create new vault.
change-vault-pwd Change vault password.
move-to-vault Move account to vault from another vault/root directory.
move-from-vault Move account to root directory from given vault.
"#;
#[derive(Debug, Deserialize)]
struct Args {
cmd_insert: bool,
cmd_change_pwd: bool,
cmd_list: bool,
cmd_import: bool,
cmd_import_wallet: bool,
cmd_find_wallet_pass: bool,
cmd_remove: bool,
cmd_sign: bool,
cmd_public: bool,
cmd_list_vaults: bool,
cmd_create_vault: bool,
cmd_change_vault_pwd: bool,
cmd_move_to_vault: bool,
cmd_move_from_vault: bool,
arg_secret: String,
arg_password: String,
arg_old_pwd: String,
arg_new_pwd: String,
arg_address: String,
arg_message: String,
arg_path: String,
arg_vault: String,
flag_src: String,
flag_dir: String,
flag_vault: String,
flag_vault_pwd: String,
}
enum Error {
Ethstore(ethstore::Error),
Docopt(docopt::Error),
}
impl From<ethstore::Error> for Error {
fn from(err: ethstore::Error) -> Self {
Error::Ethstore(err)
}
}
impl From<docopt::Error> for Error {
fn from(err: docopt::Error) -> Self {
Error::Docopt(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Ethstore(ref err) => fmt::Display::fmt(err, f),
Error::Docopt(ref err) => fmt::Display::fmt(err, f),
}
}
}
fn main() {
panic_hook::set_abort();
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "warn")
}
env_logger::try_init().expect("Logger initialized only once.");
match execute(env::args()) {
Ok(result) => println!("{}", result),
Err(Error::Docopt(ref e)) => e.exit(),
Err(err) => {
eprintln!("{}", err);
process::exit(1);
}
}
}
fn key_dir(location: &str, password: Option<Password>) -> Result<Box<dyn KeyDirectory>, Error> {
let dir: RootDiskDirectory = match location {
"geth" => RootDiskDirectory::create(dir::geth(false))?,
"geth-test" => RootDiskDirectory::create(dir::geth(true))?,
path if path.starts_with("parity") => {
let chain = path.split('-').nth(1).unwrap_or("ethereum");
let path = dir::parity(chain);
RootDiskDirectory::create(path)?
},
path => RootDiskDirectory::create(path)?,
};
Ok(Box::new(dir.with_password(password)))
}
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
if args.flag_vault.is_empty() {
return Ok(SecretVaultRef::Root);
}
let vault_pwd = load_password(&args.flag_vault_pwd)?;
store.open_vault(&args.flag_vault, &vault_pwd)?;
Ok(SecretVaultRef::Vault(args.flag_vault.clone()))
}
fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> Result<StoreAccountRef, Error> {
match open_args_vault(store, args)? {
SecretVaultRef::Root => Ok(StoreAccountRef::root(address)),
SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)),
}
}
fn format_accounts(accounts: &[Address]) -> String {
accounts.iter()
.enumerate()
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
.collect::<Vec<String>>()
.join("\n")
}
fn format_vaults(vaults: &[String]) -> String {
vaults.join("\n")
}
fn load_password(path: &str) -> Result<Password, Error> {
let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)))?;
let mut password = String::new();
file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)))?;
// drop EOF
let _ = password.pop();
Ok(password.into())
}
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).deserialize())?;
let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
return if args.cmd_insert {
let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?;
let password = load_password(&args.arg_password)?;
let vault_ref = open_args_vault(&store, &args)?;
let account_ref = store.insert_account(vault_ref, secret, &password)?;
Ok(format!("0x{:x}", account_ref.address))
} else if args.cmd_change_pwd {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let old_pwd = load_password(&args.arg_old_pwd)?;
let new_pwd = load_password(&args.arg_new_pwd)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store.change_password(&account_ref, &old_pwd, &new_pwd).is_ok();
Ok(format!("{}", ok))
} else if args.cmd_list {
let vault_ref = open_args_vault(&store, &args)?;
let accounts = store.accounts()?;
let accounts: Vec<_> = accounts
.into_iter()
.filter(|a| &a.vault == &vault_ref)
.map(|a| a.address)
.collect();
Ok(format_accounts(&accounts))
} else if args.cmd_import {
let password = match args.arg_password.as_ref() {
"" => None,
_ => Some(load_password(&args.arg_password)?)
};
let src = key_dir(&args.flag_src, password)?;
let dst = key_dir(&args.flag_dir, None)?;
let accounts = import_accounts(&*src, &*dst)?;
Ok(format_accounts(&accounts))
} else if args.cmd_import_wallet {
let wallet = PresaleWallet::open(&args.arg_path)?;
let password = load_password(&args.arg_password)?;
let kp = wallet.decrypt(&password)?;
let vault_ref = open_args_vault(&store, &args)?;
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
Ok(format!("0x{:x}", account_ref.address))
} else if args.cmd_find_wallet_pass {
let passwords = load_password(&args.arg_password)?;
let passwords = passwords.as_str().lines().map(|line| str::to_owned(line).into()).collect::<VecDeque<_>>();
crack::run(passwords, &args.arg_path)?;
Ok(format!("Password not found."))
} else if args.cmd_remove {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store.remove_account(&account_ref, &password).is_ok();
Ok(format!("{}", ok))
} else if args.cmd_sign {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let message = args.arg_message.parse().map_err(|_| ethstore::Error::InvalidMessage)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let signature = store.sign(&account_ref, &password, &message)?;
Ok(format!("0x{}", signature))
} else if args.cmd_public {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let public = store.public(&account_ref, &password)?;
Ok(format!("0x{:x}", public))
} else if args.cmd_list_vaults {
let vaults = store.list_vaults()?;
Ok(format_vaults(&vaults))
} else if args.cmd_create_vault {
let password = load_password(&args.arg_password)?;
store.create_vault(&args.arg_vault, &password)?;
Ok("OK".to_owned())
} else if args.cmd_change_vault_pwd {
let old_pwd = load_password(&args.arg_old_pwd)?;
let new_pwd = load_password(&args.arg_new_pwd)?;
store.open_vault(&args.arg_vault, &old_pwd)?;
store.change_vault_password(&args.arg_vault, &new_pwd)?;
Ok("OK".to_owned())
} else if args.cmd_move_to_vault {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?;
Ok("OK".to_owned())
} else if args.cmd_move_from_vault {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(SecretVaultRef::Root, StoreAccountRef::vault(&args.arg_vault, address))?;
Ok("OK".to_owned())
} else {
Ok(format!("{}", USAGE))
}
}

View File

@ -1,82 +0,0 @@
// Copyright 2015-2020 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/>.
extern crate tempdir;
use std::process::Command;
use tempdir::TempDir;
use std::fs::File;
use std::io::Write;
fn run(args: &[&str]) -> String {
let output = Command::new("cargo")
.args(&["run", "--"])
.args(args)
.output()
.unwrap();
assert!(output.status.success());
String::from_utf8(output.stdout).unwrap()
}
#[test]
fn cli_cmd() {
Command::new("cargo")
.arg("build")
.output()
.unwrap();
let dir = TempDir::new("test-vault").unwrap();
let mut passwd = File::create(dir.path().join("test-password")).unwrap();
writeln!(passwd, "password").unwrap();
let mut passwd2 = File::create(dir.path().join("test-vault-addr")).unwrap();
writeln!(passwd2, "password2").unwrap();
let test_password_buf = dir.path().join("test-password");
let test_password: &str = test_password_buf.to_str().unwrap();
let dir_str: &str = dir.path().to_str().unwrap();
let test_vault_addr_buf = dir.path().join("test-vault-addr");
let test_vault_addr = test_vault_addr_buf.to_str().unwrap();
run(&["create-vault", "test-vault", test_password, "--dir", dir_str]);
let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
test_vault_addr,
"--dir", dir_str,
"--vault", "test-vault",
"--vault-pwd", test_password]);
let address = output.trim();
let output = run(&["list",
"--dir", dir_str,
"--vault", "test-vault",
"--vault-pwd", test_password]);
assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n");
let output = run(&["sign", &address[2..],
test_vault_addr,
"7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
"--dir", dir_str,
"--vault", "test-vault",
"--vault-pwd", test_password]);
assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n");
let output = run(&["public", &address[2..], test_vault_addr,
"--dir", dir_str,
"--vault", "test-vault",
"--vault-pwd", test_password]);
assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n");
}

View File

@ -1,59 +0,0 @@
// Copyright 2015-2020 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/>.
use json;
#[derive(Debug, PartialEq, Clone)]
pub struct Aes128Ctr {
pub iv: [u8; 16],
}
#[derive(Debug, PartialEq, Clone)]
pub enum Cipher {
Aes128Ctr(Aes128Ctr),
}
impl From<json::Aes128Ctr> for Aes128Ctr {
fn from(json: json::Aes128Ctr) -> Self {
Aes128Ctr {
iv: json.iv.into()
}
}
}
impl Into<json::Aes128Ctr> for Aes128Ctr {
fn into(self) -> json::Aes128Ctr {
json::Aes128Ctr {
iv: From::from(self.iv)
}
}
}
impl From<json::Cipher> for Cipher {
fn from(json: json::Cipher) -> Self {
match json {
json::Cipher::Aes128Ctr(params) => Cipher::Aes128Ctr(From::from(params)),
}
}
}
impl Into<json::Cipher> for Cipher {
fn into(self) -> json::Cipher {
match self {
Cipher::Aes128Ctr(params) => json::Cipher::Aes128Ctr(params.into()),
}
}
}

View File

@ -1,207 +0,0 @@
// Copyright 2015-2020 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/>.
use std::str;
use crypto::publickey::Secret;
use ethkey::Password;
use {json, Error, crypto};
use crypto::Keccak256;
use random::Random;
use smallvec::SmallVec;
use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
/// Encrypted data
#[derive(Debug, PartialEq, Clone)]
pub struct Crypto {
/// Encryption parameters
pub cipher: Cipher,
/// Encrypted data buffer
pub ciphertext: Vec<u8>,
/// Key derivation function parameters
pub kdf: Kdf,
/// Message authentication code
pub mac: [u8; 32],
}
impl From<json::Crypto> for Crypto {
fn from(json: json::Crypto) -> Self {
Crypto {
cipher: json.cipher.into(),
ciphertext: json.ciphertext.into(),
kdf: json.kdf.into(),
mac: json.mac.into(),
}
}
}
impl From<Crypto> for json::Crypto {
fn from(c: Crypto) -> Self {
json::Crypto {
cipher: c.cipher.into(),
ciphertext: c.ciphertext.into(),
kdf: c.kdf.into(),
mac: c.mac.into(),
}
}
}
impl str::FromStr for Crypto {
type Err = <json::Crypto as str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<json::Crypto>().map(Into::into)
}
}
impl From<Crypto> for String {
fn from(c: Crypto) -> Self {
json::Crypto::from(c).into()
}
}
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)
}
/// Encrypt custom plain data
pub fn with_plain(plain: &[u8], password: &Password, iterations: u32) -> Result<Self, crypto::Error> {
let salt: [u8; 32] = Random::random();
let iv: [u8; 16] = Random::random();
// two parts of derived key
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
let (derived_left_bits, derived_right_bits) =
crypto::derive_key_iterations(password.as_bytes(), &salt, iterations);
// preallocated (on-stack in case of `Secret`) buffer to hold cipher
// length = length(plain) as we are using CTR-approach
let plain_len = plain.len();
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
// aes-128-ctr with initial vector of iv
crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?;
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
Ok(Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: iv,
}),
ciphertext: ciphertext.into_vec(),
kdf: Kdf::Pbkdf2(Pbkdf2 {
dklen: crypto::KEY_LENGTH as u32,
salt: salt.to_vec(),
c: iterations,
prf: Prf::HmacSha256,
}),
mac: mac,
})
}
/// Try to decrypt and convert result to account secret
pub fn secret(&self, password: &Password) -> Result<Secret, Error> {
if self.ciphertext.len() > 32 {
return Err(Error::InvalidSecret);
}
let secret = self.do_decrypt(password, 32)?;
Ok(Secret::import_key(&secret)?)
}
/// Try to decrypt and return result as is
pub fn decrypt(&self, password: &Password) -> Result<Vec<u8>, Error> {
let expected_len = self.ciphertext.len();
self.do_decrypt(password, expected_len)
}
fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result<Vec<u8>, Error> {
let (derived_left_bits, derived_right_bits) = match self.kdf {
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password.as_bytes(), &params.salt, params.c),
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password.as_bytes(), &params.salt, params.n, params.p, params.r)?,
};
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
if !crypto::is_equal(&mac, &self.mac) {
return Err(Error::InvalidPassword)
}
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
match self.cipher {
Cipher::Aes128Ctr(ref params) => {
// checker by callers
debug_assert!(expected_len >= self.ciphertext.len());
let from = expected_len - self.ciphertext.len();
crypto::aes::decrypt_128_ctr(&derived_left_bits, &params.iv, &self.ciphertext, &mut plain[from..])?;
Ok(plain.into_iter().collect())
},
}
}
}
#[cfg(test)]
mod tests {
use crypto::publickey::{Generator, Random};
use super::{Crypto, Error};
#[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 secret = crypto.secret(&passwd).unwrap();
assert_eq!(keypair.secret(), &secret);
}
#[test]
fn crypto_with_secret_invalid_password() {
let keypair = Random.generate().unwrap();
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), 10240).unwrap();
assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword))
}
#[test]
fn crypto_with_null_plain_data() {
let original_data = b"";
let passwd = "this is sparta".into();
let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
let decrypted_data = crypto.decrypt(&passwd).unwrap();
assert_eq!(original_data[..], *decrypted_data);
}
#[test]
fn crypto_with_tiny_plain_data() {
let original_data = b"{}";
let passwd = "this is sparta".into();
let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
let decrypted_data = crypto.decrypt(&passwd).unwrap();
assert_eq!(original_data[..], *decrypted_data);
}
#[test]
fn crypto_with_huge_plain_data() {
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
let passwd = "this is sparta".into();
let crypto = Crypto::with_plain(&original_data, &passwd, 10240).unwrap();
let decrypted_data = crypto.decrypt(&passwd).unwrap();
assert_eq!(&original_data, &decrypted_data);
}
}

View File

@ -1,125 +0,0 @@
// Copyright 2015-2020 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/>.
use json;
#[derive(Debug, PartialEq, Clone)]
pub enum Prf {
HmacSha256,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Pbkdf2 {
pub c: u32,
pub dklen: u32,
pub prf: Prf,
pub salt: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Scrypt {
pub dklen: u32,
pub p: u32,
pub n: u32,
pub r: u32,
pub salt: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Kdf {
Pbkdf2(Pbkdf2),
Scrypt(Scrypt),
}
impl From<json::Prf> for Prf {
fn from(json: json::Prf) -> Self {
match json {
json::Prf::HmacSha256 => Prf::HmacSha256,
}
}
}
impl Into<json::Prf> for Prf {
fn into(self) -> json::Prf {
match self {
Prf::HmacSha256 => json::Prf::HmacSha256,
}
}
}
impl From<json::Pbkdf2> for Pbkdf2 {
fn from(json: json::Pbkdf2) -> Self {
Pbkdf2 {
c: json.c,
dklen: json.dklen,
prf: From::from(json.prf),
salt: json.salt.into(),
}
}
}
impl Into<json::Pbkdf2> for Pbkdf2 {
fn into(self) -> json::Pbkdf2 {
json::Pbkdf2 {
c: self.c,
dklen: self.dklen,
prf: self.prf.into(),
salt: From::from(self.salt),
}
}
}
impl From<json::Scrypt> for Scrypt {
fn from(json: json::Scrypt) -> Self {
Scrypt {
dklen: json.dklen,
p: json.p,
n: json.n,
r: json.r,
salt: json.salt.into(),
}
}
}
impl Into<json::Scrypt> for Scrypt {
fn into(self) -> json::Scrypt {
json::Scrypt {
dklen: self.dklen,
p: self.p,
n: self.n,
r: self.r,
salt: From::from(self.salt),
}
}
}
impl From<json::Kdf> for Kdf {
fn from(json: json::Kdf) -> Self {
match json {
json::Kdf::Pbkdf2(params) => Kdf::Pbkdf2(From::from(params)),
json::Kdf::Scrypt(params) => Kdf::Scrypt(From::from(params)),
}
}
}
impl Into<json::Kdf> for Kdf {
fn into(self) -> json::Kdf {
match self {
Kdf::Pbkdf2(params) => json::Kdf::Pbkdf2(params.into()),
Kdf::Scrypt(params) => json::Kdf::Scrypt(params.into()),
}
}
}

View File

@ -1,230 +0,0 @@
// Copyright 2015-2020 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/>.
use crypto::publickey::{KeyPair, sign, Address, Signature, Message, Public, Secret};
use ethkey::Password;
use crypto::publickey::ecdh::agree;
use {json, Error};
use account::Version;
use crypto;
use super::crypto::Crypto;
/// Account representation.
#[derive(Debug, PartialEq, Clone)]
pub struct SafeAccount {
/// Account ID
pub id: [u8; 16],
/// Account version
pub version: Version,
/// Account address
pub address: Address,
/// Account private key derivation definition.
pub crypto: Crypto,
/// Account filename
pub filename: Option<String>,
/// Account name
pub name: String,
/// Account metadata
pub meta: String,
}
impl Into<json::KeyFile> for SafeAccount {
fn into(self) -> json::KeyFile {
json::KeyFile {
id: From::from(self.id),
version: self.version.into(),
address: Some(self.address.into()),
crypto: self.crypto.into(),
name: Some(self.name.into()),
meta: Some(self.meta.into()),
}
}
}
impl SafeAccount {
/// Create a new account
pub fn create(
keypair: &KeyPair,
id: [u8; 16],
password: &Password,
iterations: u32,
name: String,
meta: String
) -> Result<Self, crypto::Error> {
Ok(SafeAccount {
id: id,
version: Version::V3,
crypto: Crypto::with_secret(keypair.secret(), password, iterations)?,
address: keypair.address(),
filename: None,
name: name,
meta: meta,
})
}
/// Create a new `SafeAccount` from the given `json`; if it was read from a
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
/// can be left `None`.
/// In case `password` is provided, we will attempt to read the secret from the keyfile
/// and derive the address from it instead of reading it directly.
/// Providing password is required for `json::KeyFile`s with no address.
pub fn from_file(json: json::KeyFile, filename: Option<String>, password: &Option<Password>) -> Result<Self, Error> {
let crypto = Crypto::from(json.crypto);
let address = match (password, &json.address) {
(None, Some(json_address)) => json_address.into(),
(None, None) => Err(Error::Custom(
"This keystore does not contain address. You need to provide password to import it".into()))?,
(Some(password), json_address) => {
let derived_address = KeyPair::from_secret(
crypto.secret(&password).map_err(|_| Error::InvalidPassword)?
)?.address();
match json_address {
Some(json_address) => {
let json_address = json_address.into();
if derived_address != json_address {
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
derived_address, json_address);
}
},
_ => {},
}
derived_address
}
};
Ok(SafeAccount {
id: json.id.into(),
version: json.version.into(),
address,
crypto,
filename,
name: json.name.unwrap_or(String::new()),
meta: json.meta.unwrap_or("{}".to_owned()),
})
}
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
/// can be left `None`.
pub fn from_vault_file(password: &Password, json: json::VaultKeyFile, filename: Option<String>) -> Result<Self, Error> {
let meta_crypto: Crypto = json.metacrypto.into();
let meta_plain = meta_crypto.decrypt(password)?;
let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
SafeAccount::from_file(json::KeyFile {
id: json.id,
version: json.version,
crypto: json.crypto,
address: Some(meta_plain.address),
name: meta_plain.name,
meta: meta_plain.meta,
}, filename, &None)
}
/// Create a new `VaultKeyFile` from the given `self`
pub fn into_vault_file(self, iterations: u32, password: &Password) -> Result<json::VaultKeyFile, Error> {
let meta_plain = json::VaultKeyMeta {
address: self.address.into(),
name: Some(self.name),
meta: Some(self.meta),
};
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?;
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
Ok(json::VaultKeyFile {
id: self.id.into(),
version: self.version.into(),
crypto: self.crypto.into(),
metacrypto: meta_crypto.into(),
})
}
/// Sign a message.
pub fn sign(&self, password: &Password, message: &Message) -> Result<Signature, Error> {
let secret = self.crypto.secret(password)?;
sign(&secret, message).map_err(From::from)
}
/// Decrypt a message.
pub fn decrypt(&self, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
let secret = self.crypto.secret(password)?;
crypto::publickey::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
}
/// Agree on shared key.
pub fn agree(&self, password: &Password, other: &Public) -> Result<Secret, Error> {
let secret = self.crypto.secret(password)?;
agree(&secret, other).map_err(From::from)
}
/// Derive public key.
pub fn public(&self, password: &Password) -> Result<Public, Error> {
let secret = self.crypto.secret(password)?;
Ok(KeyPair::from_secret(secret)?.public().clone())
}
/// Change account's password.
pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: u32) -> Result<Self, Error> {
let secret = self.crypto.secret(old_password)?;
let result = SafeAccount {
id: self.id.clone(),
version: self.version.clone(),
crypto: Crypto::with_secret(&secret, new_password, iterations)?,
address: self.address.clone(),
filename: self.filename.clone(),
name: self.name.clone(),
meta: self.meta.clone(),
};
Ok(result)
}
/// Check if password matches the account.
pub fn check_password(&self, password: &Password) -> bool {
self.crypto.secret(password).is_ok()
}
}
#[cfg(test)]
mod tests {
use crypto::publickey::{Generator, Random, verify_public, Message};
use super::SafeAccount;
#[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 signature = account.unwrap().sign(&password, &message).unwrap();
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
}
#[test]
fn change_password() {
let keypair = Random.generate().unwrap();
let first_password = "hello world".into();
let sec_password = "this is sparta".into();
let 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();
assert!(account.sign(&first_password, &message).is_ok());
assert!(account.sign(&sec_password, &message).is_err());
assert!(new_account.sign(&first_password, &message).is_err());
assert!(new_account.sign(&sec_password, &message).is_ok());
}
}

View File

@ -1,484 +0,0 @@
// Copyright 2015-2020 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/>.
use std::{fs, io};
use std::io::Write;
use std::path::{PathBuf, Path};
use std::collections::HashMap;
use time;
use {json, SafeAccount, Error};
use json::Uuid;
use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey};
use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory};
use ethkey::Password;
const IGNORED_FILES: &'static [&'static str] = &[
"thumbs.db",
"address_book.json",
"dapps_policy.json",
"dapps_accounts.json",
"dapps_history.json",
"vault.json",
];
/// Find a unique filename that does not exist using four-letter random suffix.
pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_filename: &str) -> io::Result<String> {
let mut path = parent_path.join(original_filename);
let mut deduped_filename = original_filename.to_string();
if path.exists() {
const MAX_RETRIES: usize = 500;
let mut retries = 0;
while path.exists() {
if retries >= MAX_RETRIES {
return Err(io::Error::new(io::ErrorKind::Other, "Exceeded maximum retries when deduplicating filename."));
}
let suffix = ::random::random_string(4);
deduped_filename = format!("{}-{}", original_filename, suffix);
path.set_file_name(&deduped_filename);
retries += 1;
}
}
Ok(deduped_filename)
}
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
#[cfg(unix)]
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
use std::os::unix::fs::OpenOptionsExt;
fs::OpenOptions::new()
.write(true)
.create_new(true)
.mode((libc::S_IWUSR | libc::S_IRUSR) as u32)
.open(file_path)
}
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
#[cfg(not(unix))]
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(file_path)
}
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
#[cfg(unix)]
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
use std::os::unix::fs::PermissionsExt;
let file = fs::File::create(file_path)?;
let mut permissions = file.metadata()?.permissions();
permissions.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32);
file.set_permissions(permissions)?;
Ok(file)
}
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
#[cfg(not(unix))]
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
fs::File::create(file_path)
}
/// Root keys directory implementation
pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
/// Disk directory key file manager
pub trait KeyFileManager: Send + Sync {
/// Read `SafeAccount` from given key file stream
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read;
/// Write `SafeAccount` to given key file stream
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write;
}
/// Disk-based keys directory implementation
pub struct DiskDirectory<T> where T: KeyFileManager {
path: PathBuf,
key_manager: T,
}
/// Keys file manager for root keys directory
#[derive(Default)]
pub struct DiskKeyFileManager {
password: Option<Password>,
}
impl RootDiskDirectory {
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
fs::create_dir_all(&path)?;
Ok(Self::at(path))
}
/// allows to read keyfiles with given password (needed for keyfiles w/o address)
pub fn with_password(&self, password: Option<Password>) -> Self {
DiskDirectory::new(&self.path, DiskKeyFileManager { password })
}
pub fn at<P>(path: P) -> Self where P: AsRef<Path> {
DiskDirectory::new(path, DiskKeyFileManager::default())
}
}
impl<T> DiskDirectory<T> where T: KeyFileManager {
/// Create new disk directory instance
pub fn new<P>(path: P, key_manager: T) -> Self where P: AsRef<Path> {
DiskDirectory {
path: path.as_ref().to_path_buf(),
key_manager: key_manager,
}
}
fn files(&self) -> Result<Vec<PathBuf>, Error> {
Ok(fs::read_dir(&self.path)?
.flat_map(Result::ok)
.filter(|entry| {
let metadata = entry.metadata().ok();
let file_name = entry.file_name();
let name = file_name.to_string_lossy();
// filter directories
metadata.map_or(false, |m| !m.is_dir()) &&
// hidden files
!name.starts_with(".") &&
// other ignored files
!IGNORED_FILES.contains(&&*name)
})
.map(|entry| entry.path())
.collect::<Vec<PathBuf>>()
)
}
pub fn files_hash(&self) -> Result<u64, Error> {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
let mut hasher = DefaultHasher::new();
let files = self.files()?;
for file in files {
hasher.write(file.to_str().unwrap_or("").as_bytes())
}
Ok(hasher.finish())
}
fn last_modification_date(&self) -> Result<u64, Error> {
use std::time::{Duration, UNIX_EPOCH};
let duration = fs::metadata(&self.path)?.modified()?.duration_since(UNIX_EPOCH).unwrap_or(Duration::default());
let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64);
Ok(timestamp)
}
/// all accounts found in keys directory
fn files_content(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
// it's not done using one iterator cause
// there is an issue with rustc and it takes tooo much time to compile
let paths = self.files()?;
Ok(paths
.into_iter()
.filter_map(|path| {
let filename = Some(path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned());
fs::File::open(path.clone())
.map_err(Into::into)
.and_then(|file| self.key_manager.read(filename, file))
.map_err(|err| {
warn!("Invalid key file: {:?} ({})", path, err);
err
})
.map(|account| (path, account))
.ok()
})
.collect()
)
}
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
/// true, a random suffix is appended to the filename.
pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result<SafeAccount, Error> {
if dedup {
filename = find_unique_filename_using_random_suffix(&self.path, &filename)?;
}
// path to keyfile
let keyfile_path = self.path.join(filename.as_str());
// update account filename
let original_account = account.clone();
let mut account = account;
account.filename = Some(filename);
{
// save the file
let mut file = if dedup {
create_new_file_with_permissions_to_owner(&keyfile_path)?
} else {
replace_file_with_permissions_to_owner(&keyfile_path)?
};
// write key content
self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
file.flush()?;
file.sync_all()?;
}
Ok(account)
}
/// Get key file manager referece
pub fn key_manager(&self) -> &T {
&self.key_manager
}
}
impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
let accounts = self.files_content()?
.into_iter()
.map(|(_, account)| account)
.collect();
Ok(accounts)
}
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
// Disk store handles updates correctly iff filename is the same
let filename = account_filename(&account);
self.insert_with_filename(account, filename, false)
}
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
let filename = account_filename(&account);
self.insert_with_filename(account, filename, true)
}
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
// enumerate all entries in keystore
// and find entry with given address
let to_remove = self.files_content()?
.into_iter()
.find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address);
// remove it
match to_remove {
None => Err(Error::InvalidAccount),
Some((path, _)) => fs::remove_file(path).map_err(From::from)
}
}
fn path(&self) -> Option<&PathBuf> { Some(&self.path) }
fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> {
Some(self)
}
fn unique_repr(&self) -> Result<u64, Error> {
self.last_modification_date()
}
}
impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
fn create(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error> {
let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?;
Ok(Box::new(vault_dir))
}
fn open(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error> {
let vault_dir = VaultDiskDirectory::at(&self.path, name, key)?;
Ok(Box::new(vault_dir))
}
fn list_vaults(&self) -> Result<Vec<String>, Error> {
Ok(fs::read_dir(&self.path)?
.filter_map(|e| e.ok().map(|e| e.path()))
.filter_map(|path| {
let mut vault_file_path = path.clone();
vault_file_path.push(VAULT_FILE_NAME);
if vault_file_path.is_file() {
path.file_name().and_then(|f| f.to_str()).map(|f| f.to_owned())
} else {
None
}
})
.collect())
}
fn vault_meta(&self, name: &str) -> Result<String, Error> {
VaultDiskDirectory::meta_at(&self.path, name)
}
}
impl KeyFileManager for DiskKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
SafeAccount::from_file(key_file, filename, &self.password)
}
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {
// when account is moved back to root directory from vault
// => remove vault field from meta
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
let key_file: json::KeyFile = account.into();
key_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e)))
}
}
fn account_filename(account: &SafeAccount) -> String {
// build file path
account.filename.clone().unwrap_or_else(|| {
let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid.");
format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id))
})
}
#[cfg(test)]
mod test {
extern crate tempdir;
use std::{env, fs};
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
use account::SafeAccount;
use crypto::publickey::{Random, Generator};
use self::tempdir::TempDir;
#[test]
fn should_create_new_account() {
// given
let mut dir = env::temp_dir();
dir.push("ethstore_should_create_new_account");
let keypair = Random.generate().unwrap();
let password = "hello world".into();
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// when
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned());
let res = directory.insert(account.unwrap());
// then
assert!(res.is_ok(), "Should save account succesfuly.");
assert!(res.unwrap().filename.is_some(), "Filename has been assigned.");
// cleanup
let _ = fs::remove_dir_all(dir);
}
#[test]
fn should_handle_duplicate_filenames() {
// given
let mut dir = env::temp_dir();
dir.push("ethstore_should_handle_duplicate_filenames");
let keypair = Random.generate().unwrap();
let password = "hello world".into();
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// when
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned()).unwrap();
let filename = "test".to_string();
let dedup = true;
directory.insert_with_filename(account.clone(), "foo".to_string(), dedup).unwrap();
let file1 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
let file2 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
let file3 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
// then
// the first file should have the original names
assert_eq!(file1, filename);
// the following duplicate files should have a suffix appended
assert!(file2 != file3);
assert_eq!(file2.len(), filename.len() + 5);
assert_eq!(file3.len(), filename.len() + 5);
// cleanup
let _ = fs::remove_dir_all(dir);
}
#[test]
fn should_manage_vaults() {
// given
let mut dir = env::temp_dir();
dir.push("should_create_new_vault");
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
let vault_name = "vault";
let password = "password".into();
// then
assert!(directory.as_vault_provider().is_some());
// and when
let before_root_items_count = fs::read_dir(&dir).unwrap().count();
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, 1024));
// then
assert!(vault.is_ok());
let after_root_items_count = fs::read_dir(&dir).unwrap().count();
assert!(after_root_items_count > before_root_items_count);
// and when
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, 1024));
// then
assert!(vault.is_ok());
let after_root_items_count2 = fs::read_dir(&dir).unwrap().count();
assert!(after_root_items_count == after_root_items_count2);
// cleanup
let _ = fs::remove_dir_all(dir);
}
#[test]
fn should_list_vaults() {
// given
let temp_path = TempDir::new("").unwrap();
let directory = RootDiskDirectory::create(&temp_path).unwrap();
let vault_provider = directory.as_vault_provider().unwrap();
vault_provider.create("vault1", VaultKey::new(&"password1".into(), 1)).unwrap();
vault_provider.create("vault2", VaultKey::new(&"password2".into(), 1)).unwrap();
// then
let vaults = vault_provider.list_vaults().unwrap();
assert_eq!(vaults.len(), 2);
assert!(vaults.iter().any(|v| &*v == "vault1"));
assert!(vaults.iter().any(|v| &*v == "vault2"));
}
#[test]
fn hash_of_files() {
let temp_path = TempDir::new("").unwrap();
let directory = RootDiskDirectory::create(&temp_path).unwrap();
let hash = directory.files_hash().expect("Files hash should be calculated ok");
assert_eq!(
hash,
15130871412783076140
);
let keypair = Random.generate().unwrap();
let password = "test pass".into();
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned());
directory.insert(account.unwrap()).expect("Account should be inserted ok");
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");
assert!(new_hash != hash, "hash of the file list should change once directory content changed");
}
}

View File

@ -1,74 +0,0 @@
// Copyright 2015-2020 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/>.
use std::collections::HashMap;
use parking_lot::RwLock;
use itertools;
use crypto::publickey::Address;
use {SafeAccount, Error};
use super::KeyDirectory;
/// Accounts in-memory storage.
#[derive(Default)]
pub struct MemoryDirectory {
accounts: RwLock<HashMap<Address, Vec<SafeAccount>>>,
}
impl KeyDirectory for MemoryDirectory {
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect())
}
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
let mut lock = self.accounts.write();
let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new);
// If the filename is the same we just need to replace the entry
accounts.retain(|acc| acc.filename != account.filename);
accounts.push(account.clone());
Ok(account)
}
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
let mut lock = self.accounts.write();
let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new);
accounts.push(account.clone());
Ok(account)
}
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
let mut accounts = self.accounts.write();
let is_empty = if let Some(accounts) = accounts.get_mut(&account.address) {
if let Some(position) = accounts.iter().position(|acc| acc == account) {
accounts.remove(position);
}
accounts.is_empty()
} else {
false
};
if is_empty {
accounts.remove(&account.address);
}
Ok(())
}
fn 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() }
Ok(val)
}
}

View File

@ -1,105 +0,0 @@
// Copyright 2015-2020 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/>.
//! Accounts Directory
use ethkey::Password;
use std::path::{PathBuf};
use {SafeAccount, Error};
mod disk;
mod memory;
mod vault;
/// `VaultKeyDirectory::set_key` error
#[derive(Debug)]
pub enum SetKeyError {
/// Error is fatal and directory is probably in inconsistent state
Fatal(Error),
/// Error is non fatal, directory is reverted to pre-operation state
NonFatalOld(Error),
/// Error is non fatal, directory is consistent with new key
NonFatalNew(Error),
}
/// Vault key
#[derive(Clone, PartialEq, Eq)]
pub struct VaultKey {
/// Vault password
pub password: Password,
/// Number of iterations to produce a derived key from password
pub iterations: u32,
}
/// Keys directory
pub trait KeyDirectory: Send + Sync {
/// Read keys from directory
fn load(&self) -> Result<Vec<SafeAccount>, Error>;
/// Insert new key to directory
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
/// Update key in the directory
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
/// Remove key from directory
fn remove(&self, account: &SafeAccount) -> Result<(), Error>;
/// Get directory filesystem path, if available
fn path(&self) -> Option<&PathBuf> { None }
/// Return vault provider, if available
fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> { None }
/// Unique representation of directory account collection
fn unique_repr(&self) -> Result<u64, Error>;
}
/// Vaults provider
pub trait VaultKeyDirectoryProvider {
/// Create new vault with given key
fn create(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error>;
/// Open existing vault with given key
fn open(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error>;
/// List all vaults
fn list_vaults(&self) -> Result<Vec<String>, Error>;
/// Get vault meta
fn vault_meta(&self, name: &str) -> Result<String, Error>;
}
/// Vault directory
pub trait VaultKeyDirectory: KeyDirectory {
/// Cast to `KeyDirectory`
fn as_key_directory(&self) -> &dyn KeyDirectory;
/// Vault name
fn name(&self) -> &str;
/// Get vault key
fn key(&self) -> VaultKey;
/// Set new key for vault
fn set_key(&self, key: VaultKey) -> Result<(), SetKeyError>;
/// Get vault meta
fn meta(&self) -> String;
/// Set vault meta
fn set_meta(&self, meta: &str) -> Result<(), Error>;
}
pub use self::disk::{RootDiskDirectory, DiskKeyFileManager, KeyFileManager};
pub use self::memory::MemoryDirectory;
pub use self::vault::VaultDiskDirectory;
impl VaultKey {
/// Create new vault key
pub fn new(password: &Password, iterations: u32) -> Self {
VaultKey {
password: password.clone(),
iterations: iterations,
}
}
}

View File

@ -1,443 +0,0 @@
// Copyright 2015-2020 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/>.
use std::{fs, io};
use std::path::{PathBuf, Path};
use parking_lot::Mutex;
use {json, SafeAccount, Error};
use crypto::Keccak256;
use super::super::account::Crypto;
use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
use super::disk::{self, DiskDirectory, KeyFileManager};
/// Name of vault metadata file
pub const VAULT_FILE_NAME: &'static str = "vault.json";
/// Name of temporary vault metadata file
pub const VAULT_TEMP_FILE_NAME: &'static str = "vault_temp.json";
/// Vault directory implementation
pub type VaultDiskDirectory = DiskDirectory<VaultKeyFileManager>;
/// Vault key file manager
pub struct VaultKeyFileManager {
name: String,
key: VaultKey,
meta: Mutex<String>,
}
impl VaultDiskDirectory {
/// Create new vault directory with given key
pub fn create<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error> where P: AsRef<Path> {
// check that vault directory does not exists
let vault_dir_path = make_vault_dir_path(root, name, true)?;
if vault_dir_path.exists() {
return Err(Error::CreationFailed);
}
// create vault && vault file
let vault_meta = "{}";
fs::create_dir_all(&vault_dir_path)?;
if let Err(err) = create_vault_file(&vault_dir_path, &key, vault_meta) {
let _ = fs::remove_dir_all(&vault_dir_path); // can't do anything with this
return Err(err);
}
Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, vault_meta)))
}
/// Open existing vault directory with given key
pub fn at<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error> where P: AsRef<Path> {
// check that vault directory exists
let vault_dir_path = make_vault_dir_path(root, name, true)?;
if !vault_dir_path.is_dir() {
return Err(Error::CreationFailed);
}
// check that passed key matches vault file
let meta = read_vault_file(&vault_dir_path, Some(&key))?;
Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, &meta)))
}
/// Read vault meta without actually opening the vault
pub fn meta_at<P>(root: P, name: &str) -> Result<String, Error> where P: AsRef<Path> {
// check that vault directory exists
let vault_dir_path = make_vault_dir_path(root, name, true)?;
if !vault_dir_path.is_dir() {
return Err(Error::VaultNotFound);
}
// check that passed key matches vault file
read_vault_file(&vault_dir_path, None)
}
fn create_temp_vault(&self, key: VaultKey) -> Result<VaultDiskDirectory, Error> {
let original_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
let mut path: PathBuf = original_path.clone();
let name = self.name();
path.push(name); // to jump to the next level
let mut index = 0;
loop {
let name = format!("{}_temp_{}", name, index);
path.set_file_name(&name);
if !path.exists() {
return VaultDiskDirectory::create(original_path, &name, key);
}
index += 1;
}
}
fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> {
for account in self.load()? {
let filename = account.filename.clone().expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed");
vault.insert_with_filename(account, filename, true)?;
}
Ok(())
}
fn delete(&self) -> Result<(), Error> {
let path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
fs::remove_dir_all(path).map_err(Into::into)
}
}
impl VaultKeyDirectory for VaultDiskDirectory {
fn as_key_directory(&self) -> &dyn KeyDirectory {
self
}
fn name(&self) -> &str {
&self.key_manager().name
}
fn key(&self) -> VaultKey {
self.key_manager().key.clone()
}
fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> {
let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()).map_err(|err| SetKeyError::NonFatalOld(err))?;
let mut source_path = temp_vault.path().expect("temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed").clone();
let mut target_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed").clone();
// preserve meta
temp_vault.set_meta(&self.meta()).map_err(SetKeyError::NonFatalOld)?;
// jump to next fs level
source_path.push("next");
target_path.push("next");
let temp_accounts = self.copy_to_vault(&temp_vault)
.and_then(|_| temp_vault.load())
.map_err(|err| {
// ignore error, as we already processing error
let _ = temp_vault.delete();
SetKeyError::NonFatalOld(err)
})?;
// we can't just delete temp vault until all files moved, because
// original vault content has already been partially replaced
// => when error or crash happens here, we can't do anything
for temp_account in temp_accounts {
let filename = temp_account.filename.expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed");
source_path.set_file_name(&filename);
target_path.set_file_name(&filename);
fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
}
source_path.set_file_name(VAULT_FILE_NAME);
target_path.set_file_name(VAULT_FILE_NAME);
fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
temp_vault.delete().map_err(|err| SetKeyError::NonFatalNew(err))
}
fn meta(&self) -> String {
self.key_manager().meta.lock().clone()
}
fn set_meta(&self, meta: &str) -> Result<(), Error> {
let key_manager = self.key_manager();
let vault_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
create_vault_file(vault_path, &key_manager.key, meta)?;
*key_manager.meta.lock() = meta.to_owned();
Ok(())
}
}
impl VaultKeyFileManager {
pub fn new(name: &str, key: VaultKey, meta: &str) -> Self {
VaultKeyFileManager {
name: name.into(),
key: key,
meta: Mutex::new(meta.to_owned()),
}
}
}
impl KeyFileManager for VaultKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
let vault_file = json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
let mut safe_account = SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?;
safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name)
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
Ok(safe_account)
}
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
let vault_file: json::VaultKeyFile = account.into_vault_file(self.key.iterations, &self.key.password)?;
vault_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e)))
}
}
/// Makes path to vault directory, checking that vault name is appropriate
fn make_vault_dir_path<P>(root: P, name: &str, check_name: bool) -> Result<PathBuf, Error> where P: AsRef<Path> {
// check vault name
if check_name && !check_vault_name(name) {
return Err(Error::InvalidVaultName);
}
let mut vault_dir_path: PathBuf = root.as_ref().into();
vault_dir_path.push(name);
Ok(vault_dir_path)
}
/// Every vault must have unique name => we rely on filesystem to check this
/// => vault name must not contain any fs-special characters to avoid directory traversal
/// => we only allow alphanumeric + separator characters in vault name.
fn check_vault_name(name: &str) -> bool {
!name.is_empty()
&& name.chars()
.all(|c| c.is_alphanumeric()
|| c.is_whitespace()
|| c == '-' || c == '_')
}
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> {
let password_hash = key.password.as_bytes().keccak256();
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME);
let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(vault_dir_path.as_ref(), &VAULT_TEMP_FILE_NAME)?;
let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name);
// this method is used to rewrite existing vault file
// => write to temporary file first, then rename temporary file to vault file
let mut vault_file = disk::create_new_file_with_permissions_to_owner(&temp_vault_file_path)?;
let vault_file_contents = json::VaultFile {
crypto: crypto.into(),
meta: Some(meta.to_owned()),
};
vault_file_contents.write(&mut vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
drop(vault_file);
fs::rename(&temp_vault_file_path, &vault_file_path)?;
Ok(())
}
/// When vault is opened => we must check that password matches && read metadata
fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<String, Error> where P: AsRef<Path> {
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
vault_file_path.push(VAULT_FILE_NAME);
let vault_file = fs::File::open(vault_file_path)?;
let vault_file_contents = json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned());
let vault_file_crypto: Crypto = vault_file_contents.crypto.into();
if let Some(key) = key {
let password_bytes = vault_file_crypto.decrypt(&key.password)?;
let password_hash = key.password.as_bytes().keccak256();
if password_hash != password_bytes.as_slice() {
return Err(Error::InvalidPassword);
}
}
Ok(vault_file_meta)
}
#[cfg(test)]
mod test {
extern crate tempdir;
use std::fs;
use std::io::Write;
use std::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;
#[test]
fn check_vault_name_succeeds() {
assert!(check_vault_name("vault"));
assert!(check_vault_name("vault with spaces"));
assert!(check_vault_name("vault with tabs"));
assert!(check_vault_name("vault_with_underscores"));
assert!(check_vault_name("vault-with-dashes"));
assert!(check_vault_name("vault-with-digits-123"));
assert!(check_vault_name("vault中文名字"));
}
#[test]
fn check_vault_name_fails() {
assert!(!check_vault_name(""));
assert!(!check_vault_name("."));
assert!(!check_vault_name("*"));
assert!(!check_vault_name("../.bash_history"));
assert!(!check_vault_name("/etc/passwd"));
assert!(!check_vault_name("c:\\windows"));
}
#[test]
fn make_vault_dir_path_succeeds() {
use std::path::Path;
assert_eq!(&make_vault_dir_path("/home/user/parity", "vault", true).unwrap(), &Path::new("/home/user/parity/vault"));
assert_eq!(&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(), &Path::new("/home/user/parity/*bad-name*"));
}
#[test]
fn make_vault_dir_path_fails() {
assert!(make_vault_dir_path("/home/user/parity", "*bad-name*", true).is_err());
}
#[test]
fn create_vault_file_succeeds() {
// given
let temp_path = TempDir::new("").unwrap();
let key = VaultKey::new(&"password".into(), 1024);
let mut vault_dir: PathBuf = temp_path.path().into();
vault_dir.push("vault");
fs::create_dir_all(&vault_dir).unwrap();
// when
let result = create_vault_file(&vault_dir, &key, "{}");
// then
assert!(result.is_ok());
let mut vault_file_path = vault_dir.clone();
vault_file_path.push(VAULT_FILE_NAME);
assert!(vault_file_path.exists() && vault_file_path.is_file());
}
#[test]
fn read_vault_file_succeeds() {
// given
let temp_path = TempDir::new("").unwrap();
let key = VaultKey::new(&"password".into(), 1024);
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#;
let dir: PathBuf = temp_path.path().into();
let mut vault_file_path: PathBuf = dir.clone();
vault_file_path.push(VAULT_FILE_NAME);
{
let mut vault_file = fs::File::create(vault_file_path).unwrap();
vault_file.write_all(vault_file_contents.as_bytes()).unwrap();
}
// when
let result = read_vault_file(&dir, Some(&key));
// then
assert!(result.is_ok());
}
#[test]
fn read_vault_file_fails() {
// given
let temp_path = TempDir::new("").unwrap();
let key = VaultKey::new(&"password1".into(), 1024);
let dir: PathBuf = temp_path.path().into();
let mut vault_file_path: PathBuf = dir.clone();
vault_file_path.push(VAULT_FILE_NAME);
// when
let result = read_vault_file(&dir, Some(&key));
// then
assert!(result.is_err());
// and when given
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#;
{
let mut vault_file = fs::File::create(vault_file_path).unwrap();
vault_file.write_all(vault_file_contents.as_bytes()).unwrap();
}
// when
let result = read_vault_file(&dir, Some(&key));
// then
assert!(result.is_err());
}
#[test]
fn vault_directory_can_be_created() {
// given
let temp_path = TempDir::new("").unwrap();
let key = VaultKey::new(&"password".into(), 1024);
let dir: PathBuf = temp_path.path().into();
// when
let vault = VaultDiskDirectory::create(&dir, "vault", key.clone());
// then
assert!(vault.is_ok());
// and when
let vault = VaultDiskDirectory::at(&dir, "vault", key);
// then
assert!(vault.is_ok());
}
#[test]
fn vault_directory_cannot_be_created_if_already_exists() {
// given
let temp_path = TempDir::new("").unwrap();
let key = VaultKey::new(&"password".into(), 1024);
let dir: PathBuf = temp_path.path().into();
let mut vault_dir = dir.clone();
vault_dir.push("vault");
fs::create_dir_all(&vault_dir).unwrap();
// when
let vault = VaultDiskDirectory::create(&dir, "vault", key);
// then
assert!(vault.is_err());
}
#[test]
fn vault_directory_cannot_be_opened_if_not_exists() {
// given
let temp_path = TempDir::new("").unwrap();
let key = VaultKey::new(&"password".into(), 1024);
let dir: PathBuf = temp_path.path().into();
// when
let vault = VaultDiskDirectory::at(&dir, "vault", key);
// then
assert!(vault.is_err());
}
}

View File

@ -1,118 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
use std::io::Error as IoError;
use crypto::{self, Error as EthCryptoError};
use crypto::publickey::{Error as EthPublicKeyCryptoError, DerivationError};
/// Account-related errors.
#[derive(Debug)]
pub enum Error {
/// IO error
Io(IoError),
/// Invalid Password
InvalidPassword,
/// Account's secret is invalid.
InvalidSecret,
/// Invalid Vault Crypto meta.
InvalidCryptoMeta,
/// Invalid Account.
InvalidAccount,
/// Invalid Message.
InvalidMessage,
/// Invalid Key File
InvalidKeyFile(String),
/// Vaults are not supported.
VaultsAreNotSupported,
/// Unsupported vault
UnsupportedVault,
/// Invalid vault name
InvalidVaultName,
/// Vault not found
VaultNotFound,
/// Account creation failed.
CreationFailed,
/// `EthCrypto` error
EthCrypto(EthCryptoError),
/// `EthPublicKeyCryptoError` error
EthPublicKeyCrypto(EthPublicKeyCryptoError),
/// Derivation error
Derivation(DerivationError),
/// Custom error
Custom(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let s = match *self {
Error::Io(ref err) => err.to_string(),
Error::InvalidPassword => "Invalid password".into(),
Error::InvalidSecret => "Invalid secret".into(),
Error::InvalidCryptoMeta => "Invalid crypted metadata".into(),
Error::InvalidAccount => "Invalid account".into(),
Error::InvalidMessage => "Invalid message".into(),
Error::InvalidKeyFile(ref reason) => format!("Invalid key file: {}", reason),
Error::VaultsAreNotSupported => "Vaults are not supported".into(),
Error::UnsupportedVault => "Vault is not supported for this operation".into(),
Error::InvalidVaultName => "Invalid vault name".into(),
Error::VaultNotFound => "Vault not found".into(),
Error::CreationFailed => "Account creation failed".into(),
Error::EthCrypto(ref err) => err.to_string(),
Error::EthPublicKeyCrypto(ref err) => err.to_string(),
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
Error::Custom(ref s) => s.clone(),
};
write!(f, "{}", s)
}
}
impl From<IoError> for Error {
fn from(err: IoError) -> Self {
Error::Io(err)
}
}
impl From<EthPublicKeyCryptoError> for Error {
fn from(err: EthPublicKeyCryptoError) -> Self {
Error::EthPublicKeyCrypto(err)
}
}
impl From<EthCryptoError> for Error {
fn from(err: EthCryptoError) -> Self {
Error::EthCrypto(err)
}
}
impl From<crypto::error::ScryptError> for Error {
fn from(err: crypto::error::ScryptError) -> Self {
Error::EthCrypto(err.into())
}
}
impl From<crypto::error::SymmError> for Error {
fn from(err: crypto::error::SymmError) -> Self {
Error::EthCrypto(err.into())
}
}
impl From<DerivationError> for Error {
fn from(err: DerivationError) -> Self {
Error::Derivation(err)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,80 +0,0 @@
// Copyright 2015-2020 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/>.
use std::collections::HashSet;
use std::path::Path;
use std::fs;
use crypto::publickey::Address;
use accounts_dir::{KeyDirectory, RootDiskDirectory, DiskKeyFileManager, KeyFileManager};
use dir;
use Error;
/// Import an account from a file.
pub fn import_account(path: &Path, dst: &dyn KeyDirectory) -> Result<Address, Error> {
let key_manager = DiskKeyFileManager::default();
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned());
let account = fs::File::open(&path)
.map_err(Into::into)
.and_then(|file| key_manager.read(filename, file))?;
let address = account.address.clone();
if !existing_accounts.contains(&address) {
dst.insert(account)?;
}
Ok(address)
}
/// Import all accounts from one directory to the other.
pub fn import_accounts(src: &dyn KeyDirectory, dst: &dyn KeyDirectory) -> Result<Vec<Address>, Error> {
let accounts = src.load()?;
let existing_accounts = dst.load()?.into_iter()
.map(|a| a.address)
.collect::<HashSet<_>>();
accounts.into_iter()
.filter(|a| !existing_accounts.contains(&a.address))
.map(|a| {
let address = a.address.clone();
dst.insert(a)?;
Ok(address)
}).collect()
}
/// Provide a `HashSet` of all accounts available for import from the Geth keystore.
pub fn read_geth_accounts(testnet: bool) -> Vec<Address> {
RootDiskDirectory::at(dir::geth(testnet))
.load()
.map(|d| d.into_iter().map(|a| a.address).collect())
.unwrap_or_else(|_| Vec::new())
}
/// Import specific `desired` accounts from the Geth keystore into `dst`.
pub fn import_geth_accounts(dst: &dyn KeyDirectory, desired: HashSet<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
let src = RootDiskDirectory::at(dir::geth(testnet));
let accounts = src.load()?;
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
accounts.into_iter()
.filter(|a| !existing_accounts.contains(&a.address))
.filter(|a| desired.contains(&a.address))
.map(|a| {
let address = a.address.clone();
dst.insert(a)?;
Ok(address)
}).collect()
}

View File

@ -1,74 +0,0 @@
// Copyright 2015-2020 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/>.
use std::{ops, str};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::Error;
use rustc_hex::{ToHex, FromHex, FromHexError};
#[derive(Debug, PartialEq)]
pub struct Bytes(Vec<u8>);
impl ops::Deref for Bytes {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> Deserialize<'a> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a>
{
let s = String::deserialize(deserializer)?;
let data = s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?;
Ok(Bytes(data))
}
}
impl Serialize for Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
serializer.serialize_str(&self.0.to_hex())
}
}
impl str::FromStr for Bytes {
type Err = FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.from_hex().map(Bytes)
}
}
impl From<&'static str> for Bytes {
fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
}
}
impl From<Vec<u8>> for Bytes {
fn from(v: Vec<u8>) -> Self {
Bytes(v)
}
}
impl From<Bytes> for Vec<u8> {
fn from(b: Bytes) -> Self {
b.0
}
}

View File

@ -1,96 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError};
use super::{Error, H128};
#[derive(Debug, PartialEq)]
pub enum CipherSer {
Aes128Ctr,
}
impl Serialize for CipherSer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match *self {
CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"),
}
}
}
impl<'a> Deserialize<'a> for CipherSer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
deserializer.deserialize_any(CipherSerVisitor)
}
}
struct CipherSerVisitor;
impl<'a> Visitor<'a> for CipherSerVisitor {
type Value = CipherSer;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid cipher identifier")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
match value {
"aes-128-ctr" => Ok(CipherSer::Aes128Ctr),
_ => Err(SerdeError::custom(Error::UnsupportedCipher))
}
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
self.visit_str(value.as_ref())
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Aes128Ctr {
pub iv: H128,
}
#[derive(Debug, PartialEq)]
pub enum CipherSerParams {
Aes128Ctr(Aes128Ctr),
}
impl Serialize for CipherSerParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match *self {
CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer),
}
}
}
impl<'a> Deserialize<'a> for CipherSerParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
Aes128Ctr::deserialize(deserializer)
.map(CipherSerParams::Aes128Ctr)
.map_err(|_| Error::InvalidCipherParams)
.map_err(SerdeError::custom)
}
}
#[derive(Debug, PartialEq)]
pub enum Cipher {
Aes128Ctr(Aes128Ctr),
}

View File

@ -1,194 +0,0 @@
// Copyright 2015-2020 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/>.
use std::{fmt, str};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::ser::SerializeStruct;
use serde::de::{Visitor, MapAccess, Error};
use serde_json;
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes};
pub type CipherText = Bytes;
#[derive(Debug, PartialEq)]
pub struct Crypto {
pub cipher: Cipher,
pub ciphertext: CipherText,
pub kdf: Kdf,
pub mac: H256,
}
impl str::FromStr for Crypto {
type Err = serde_json::error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
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")
}
}
enum CryptoField {
Cipher,
CipherParams,
CipherText,
Kdf,
KdfParams,
Mac,
Version,
}
impl<'a> Deserialize<'a> for CryptoField {
fn deserialize<D>(deserializer: D) -> Result<CryptoField, D::Error>
where D: Deserializer<'a>
{
deserializer.deserialize_any(CryptoFieldVisitor)
}
}
struct CryptoFieldVisitor;
impl<'a> Visitor<'a> for CryptoFieldVisitor {
type Value = CryptoField;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid crypto struct description")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: Error
{
match value {
"cipher" => Ok(CryptoField::Cipher),
"cipherparams" => Ok(CryptoField::CipherParams),
"ciphertext" => Ok(CryptoField::CipherText),
"kdf" => Ok(CryptoField::Kdf),
"kdfparams" => Ok(CryptoField::KdfParams),
"mac" => Ok(CryptoField::Mac),
"version" => Ok(CryptoField::Version),
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
}
}
}
impl<'a> Deserialize<'a> for Crypto {
fn deserialize<D>(deserializer: D) -> Result<Crypto, D::Error>
where D: Deserializer<'a>
{
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor)
}
}
struct CryptoVisitor;
impl<'a> Visitor<'a> for CryptoVisitor {
type Value = Crypto;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid vault crypto object")
}
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where V: MapAccess<'a>
{
let mut cipher = None;
let mut cipherparams = None;
let mut ciphertext = None;
let mut kdf = None;
let mut kdfparams = None;
let mut mac = None;
loop {
match visitor.next_key()? {
Some(CryptoField::Cipher) => { cipher = Some(visitor.next_value()?); }
Some(CryptoField::CipherParams) => { cipherparams = Some(visitor.next_value()?); }
Some(CryptoField::CipherText) => { ciphertext = Some(visitor.next_value()?); }
Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); }
Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); }
Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); }
// skip not required version field (it appears in pyethereum generated keystores)
Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) }
None => { break; }
}
}
let cipher = match (cipher, cipherparams) {
(Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => Cipher::Aes128Ctr(params),
(None, _) => return Err(V::Error::missing_field("cipher")),
(Some(_), None) => return Err(V::Error::missing_field("cipherparams")),
};
let ciphertext = match ciphertext {
Some(ciphertext) => ciphertext,
None => return Err(V::Error::missing_field("ciphertext")),
};
let kdf = match (kdf, kdfparams) {
(Some(KdfSer::Pbkdf2), Some(KdfSerParams::Pbkdf2(params))) => Kdf::Pbkdf2(params),
(Some(KdfSer::Scrypt), Some(KdfSerParams::Scrypt(params))) => Kdf::Scrypt(params),
(Some(_), Some(_)) => return Err(V::Error::custom("Invalid cipherparams")),
(None, _) => return Err(V::Error::missing_field("kdf")),
(Some(_), None) => return Err(V::Error::missing_field("kdfparams")),
};
let mac = match mac {
Some(mac) => mac,
None => return Err(V::Error::missing_field("mac")),
};
let result = Crypto {
cipher: cipher,
ciphertext: ciphertext,
kdf: kdf,
mac: mac,
};
Ok(result)
}
}
impl Serialize for Crypto {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
let mut crypto = serializer.serialize_struct("Crypto", 6)?;
match self.cipher {
Cipher::Aes128Ctr(ref params) => {
crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?;
crypto.serialize_field("cipherparams", params)?;
},
}
crypto.serialize_field("ciphertext", &self.ciphertext)?;
match self.kdf {
Kdf::Pbkdf2(ref params) => {
crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?;
crypto.serialize_field("kdfparams", params)?;
},
Kdf::Scrypt(ref params) => {
crypto.serialize_field("kdf", &KdfSer::Scrypt)?;
crypto.serialize_field("kdfparams", params)?;
},
}
crypto.serialize_field("mac", &self.mac)?;
crypto.end()
}
}

View File

@ -1,50 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum Error {
UnsupportedCipher,
InvalidCipherParams,
UnsupportedKdf,
InvalidUuid,
UnsupportedVersion,
InvalidCiphertext,
InvalidH256,
InvalidPrf,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Error::InvalidUuid => write!(f, "Invalid Uuid"),
Error::UnsupportedVersion => write!(f, "Unsupported version"),
Error::UnsupportedKdf => write!(f, "Unsupported kdf"),
Error::InvalidCiphertext => write!(f, "Invalid ciphertext"),
Error::UnsupportedCipher => write!(f, "Unsupported cipher"),
Error::InvalidCipherParams => write!(f, "Invalid cipher params"),
Error::InvalidH256 => write!(f, "Invalid hash"),
Error::InvalidPrf => write!(f, "Invalid prf"),
}
}
}
impl Into<String> for Error {
fn into(self) -> String {
format!("{}", self)
}
}

View File

@ -1,119 +0,0 @@
// Copyright 2015-2020 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/>.
use std::{ops, fmt, str};
use rustc_hex::{FromHex, ToHex};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError};
use super::Error;
macro_rules! impl_hash {
($name: ident, $size: expr) => {
pub struct $name([u8; $size]);
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let self_ref: &[u8] = &self.0;
write!(f, "{:?}", self_ref)
}
}
impl PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
let self_ref: &[u8] = &self.0;
let other_ref: &[u8] = &other.0;
self_ref == other_ref
}
}
impl ops::Deref for $name {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
serializer.serialize_str(&self.0.to_hex())
}
}
impl<'a> Deserialize<'a> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
struct HashVisitor;
impl<'b> Visitor<'b> for HashVisitor {
type Value = $name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a hex-encoded {}", stringify!($name))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
value.parse().map_err(SerdeError::custom)
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
self.visit_str(value.as_ref())
}
}
deserializer.deserialize_any(HashVisitor)
}
}
impl str::FromStr for $name {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value.from_hex() {
Ok(ref hex) if hex.len() == $size => {
let mut hash = [0u8; $size];
hash.clone_from_slice(hex);
Ok($name(hash))
}
_ => Err(Error::InvalidH256),
}
}
}
impl From<&'static str> for $name {
fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!($name), s))
}
}
impl From<[u8; $size]> for $name {
fn from(bytes: [u8; $size]) -> Self {
$name(bytes)
}
}
impl Into<[u8; $size]> for $name {
fn into(self) -> [u8; $size] {
self.0
}
}
}
}
impl_hash!(H128, 16);
impl_hash!(H160, 20);
impl_hash!(H256, 32);

View File

@ -1,153 +0,0 @@
// Copyright 2015-2020 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/>.
//! Universaly unique identifier.
use std::{fmt, str};
use rustc_hex::{ToHex, FromHex};
use serde::{Deserialize, Serialize, Deserializer, Serializer};
use serde::de::{Visitor, Error as SerdeError};
use super::Error;
/// Universaly unique identifier.
#[derive(Debug, PartialEq)]
pub struct Uuid([u8; 16]);
impl From<[u8; 16]> for Uuid {
fn from(uuid: [u8; 16]) -> Self {
Uuid(uuid)
}
}
impl<'a> Into<String> for &'a Uuid {
fn into(self) -> String {
let d1 = &self.0[0..4];
let d2 = &self.0[4..6];
let d3 = &self.0[6..8];
let d4 = &self.0[8..10];
let d5 = &self.0[10..16];
[d1, d2, d3, d4, d5].iter().map(|d| d.to_hex()).collect::<Vec<String>>().join("-")
}
}
impl Into<String> for Uuid {
fn into(self) -> String {
Into::into(&self)
}
}
impl Into<[u8; 16]> for Uuid {
fn into(self) -> [u8; 16] {
self.0
}
}
impl fmt::Display for Uuid {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let s: String = (self as &Uuid).into();
write!(f, "{}", s)
}
}
fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> {
let from = from.from_hex().map_err(|_| Error::InvalidUuid)?;
if from.len() != into.len() {
return Err(Error::InvalidUuid);
}
into.copy_from_slice(&from);
Ok(())
}
impl str::FromStr for Uuid {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split("-").collect();
if parts.len() != 5 {
return Err(Error::InvalidUuid);
}
let mut uuid = [0u8; 16];
copy_into(parts[0], &mut uuid[0..4])?;
copy_into(parts[1], &mut uuid[4..6])?;
copy_into(parts[2], &mut uuid[6..8])?;
copy_into(parts[3], &mut uuid[8..10])?;
copy_into(parts[4], &mut uuid[10..16])?;
Ok(Uuid(uuid))
}
}
impl From<&'static str> for Uuid {
fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
}
}
impl Serialize for Uuid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let s: String = self.into();
serializer.serialize_str(&s)
}
}
impl<'a> Deserialize<'a> for Uuid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
deserializer.deserialize_any(UuidVisitor)
}
}
struct UuidVisitor;
impl<'a> Visitor<'a> for UuidVisitor {
type Value = Uuid;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid hex-encoded UUID")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
value.parse().map_err(SerdeError::custom)
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
self.visit_str(value.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::Uuid;
#[test]
fn uuid_from_str() {
let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into();
assert_eq!(uuid, Uuid::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6]));
}
#[test]
fn uuid_from_and_to_str() {
let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6";
let uuid: Uuid = from.into();
let to: String = uuid.into();
assert_eq!(from, &to);
}
}

View File

@ -1,159 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError};
use super::{Error, Bytes};
#[derive(Debug, PartialEq)]
pub enum KdfSer {
Pbkdf2,
Scrypt,
}
impl Serialize for KdfSer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match *self {
KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"),
KdfSer::Scrypt => serializer.serialize_str("scrypt"),
}
}
}
impl<'a> Deserialize<'a> for KdfSer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
deserializer.deserialize_any(KdfSerVisitor)
}
}
struct KdfSerVisitor;
impl<'a> Visitor<'a> for KdfSerVisitor {
type Value = KdfSer;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a kdf algorithm identifier")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
match value {
"pbkdf2" => Ok(KdfSer::Pbkdf2),
"scrypt" => Ok(KdfSer::Scrypt),
_ => Err(SerdeError::custom(Error::UnsupportedKdf))
}
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
self.visit_str(value.as_ref())
}
}
#[derive(Debug, PartialEq)]
pub enum Prf {
HmacSha256,
}
impl Serialize for Prf {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match *self {
Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"),
}
}
}
impl<'a> Deserialize<'a> for Prf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
deserializer.deserialize_any(PrfVisitor)
}
}
struct PrfVisitor;
impl<'a> Visitor<'a> for PrfVisitor {
type Value = Prf;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a prf algorithm identifier")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
match value {
"hmac-sha256" => Ok(Prf::HmacSha256),
_ => Err(SerdeError::custom(Error::InvalidPrf)),
}
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
self.visit_str(value.as_ref())
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Pbkdf2 {
pub c: u32,
pub dklen: u32,
pub prf: Prf,
pub salt: Bytes,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Scrypt {
pub dklen: u32,
pub p: u32,
pub n: u32,
pub r: u32,
pub salt: Bytes,
}
#[derive(Debug, PartialEq)]
pub enum KdfSerParams {
Pbkdf2(Pbkdf2),
Scrypt(Scrypt),
}
impl Serialize for KdfSerParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match *self {
KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer),
KdfSerParams::Scrypt(ref params) => params.serialize(serializer),
}
}
}
impl<'a> Deserialize<'a> for KdfSerParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
use serde_json::{Value, from_value};
let v: Value = Deserialize::deserialize(deserializer)?;
from_value(v.clone()).map(KdfSerParams::Pbkdf2)
.or_else(|_| from_value(v).map(KdfSerParams::Scrypt))
.map_err(|_| D::Error::custom("Invalid KDF algorithm"))
}
}
#[derive(Debug, PartialEq)]
pub enum Kdf {
Pbkdf2(Pbkdf2),
Scrypt(Scrypt),
}

View File

@ -1,324 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
use std::io::{Read, Write};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Error, Visitor, MapAccess, DeserializeOwned};
use serde_json;
use super::{Uuid, Version, Crypto, H160};
/// Public opaque type representing serializable `KeyFile`.
#[derive(Debug, PartialEq)]
pub struct OpaqueKeyFile {
key_file: KeyFile
}
impl Serialize for OpaqueKeyFile {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: Serializer,
{
self.key_file.serialize(serializer)
}
}
impl<T> From<T> for OpaqueKeyFile where T: Into<KeyFile> {
fn from(val: T) -> Self {
OpaqueKeyFile { key_file: val.into() }
}
}
#[derive(Debug, PartialEq, Serialize)]
pub struct KeyFile {
pub id: Uuid,
pub version: Version,
pub crypto: Crypto,
pub address: Option<H160>,
pub name: Option<String>,
pub meta: Option<String>,
}
enum KeyFileField {
Id,
Version,
Crypto,
Address,
Name,
Meta,
}
impl<'a> Deserialize<'a> for KeyFileField {
fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error>
where D: Deserializer<'a>
{
deserializer.deserialize_any(KeyFileFieldVisitor)
}
}
struct KeyFileFieldVisitor;
impl<'a> Visitor<'a> for KeyFileFieldVisitor {
type Value = KeyFileField;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid key file field")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: Error
{
match value {
"id" => Ok(KeyFileField::Id),
"version" => Ok(KeyFileField::Version),
"crypto" => Ok(KeyFileField::Crypto),
"Crypto" => Ok(KeyFileField::Crypto),
"address" => Ok(KeyFileField::Address),
"name" => Ok(KeyFileField::Name),
"meta" => Ok(KeyFileField::Meta),
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
}
}
}
impl<'a> Deserialize<'a> for KeyFile {
fn deserialize<D>(deserializer: D) -> Result<KeyFile, D::Error>
where D: Deserializer<'a>
{
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor)
}
}
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T> where
T: DeserializeOwned
{
v.and_then(|v| if v.is_null() {
None
} else {
serde_json::from_value(v).ok()
})
}
struct KeyFileVisitor;
impl<'a> Visitor<'a> for KeyFileVisitor {
type Value = KeyFile;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid key object")
}
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where V: MapAccess<'a>
{
let mut id = None;
let mut version = None;
let mut crypto = None;
let mut address = None;
let mut name = None;
let mut meta = None;
loop {
match visitor.next_key()? {
Some(KeyFileField::Id) => { id = Some(visitor.next_value()?); }
Some(KeyFileField::Version) => { version = Some(visitor.next_value()?); }
Some(KeyFileField::Crypto) => { crypto = Some(visitor.next_value()?); }
Some(KeyFileField::Address) => { address = Some(visitor.next_value()?); }
Some(KeyFileField::Name) => { name = none_if_empty(visitor.next_value().ok()) }
Some(KeyFileField::Meta) => { meta = none_if_empty(visitor.next_value().ok()) }
None => { break; }
}
}
let id = match id {
Some(id) => id,
None => return Err(V::Error::missing_field("id")),
};
let version = match version {
Some(version) => version,
None => return Err(V::Error::missing_field("version")),
};
let crypto = match crypto {
Some(crypto) => crypto,
None => return Err(V::Error::missing_field("crypto")),
};
let result = KeyFile {
id: id,
version: version,
crypto: crypto,
address: address,
name: name,
meta: meta,
};
Ok(result)
}
}
impl KeyFile {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
serde_json::from_reader(reader)
}
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write {
serde_json::to_writer(writer, self)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use serde_json;
use json::{KeyFile, Uuid, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt};
#[test]
fn basic_keyfile() {
let json = r#"
{
"address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
"crypto": {
"cipher": "aes-128-ctr",
"ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
"cipherparams": {
"iv": "b5a7ec855ec9e2c405371356855fec83"
},
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
},
"mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
},
"id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
"version": 3,
"name": "Test",
"meta": "{}"
}"#;
let expected = KeyFile {
id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
version: Version::V3,
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
kdf: Kdf::Scrypt(Scrypt {
n: 262144,
dklen: 32,
p: 1,
r: 8,
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
}),
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
},
name: Some("Test".to_owned()),
meta: Some("{}".to_owned()),
};
let keyfile: KeyFile = serde_json::from_str(json).unwrap();
assert_eq!(keyfile, expected);
}
#[test]
fn capital_crypto_keyfile() {
let json = r#"
{
"address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
"Crypto": {
"cipher": "aes-128-ctr",
"ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
"cipherparams": {
"iv": "b5a7ec855ec9e2c405371356855fec83"
},
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
},
"mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
},
"id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
"version": 3
}"#;
let expected = KeyFile {
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
version: Version::V3,
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
kdf: Kdf::Scrypt(Scrypt {
n: 262144,
dklen: 32,
p: 1,
r: 8,
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
}),
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
},
name: None,
meta: None,
};
let keyfile: KeyFile = serde_json::from_str(json).unwrap();
assert_eq!(keyfile, expected);
}
#[test]
fn to_and_from_json() {
let file = KeyFile {
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
version: Version::V3,
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
kdf: Kdf::Scrypt(Scrypt {
n: 262144,
dklen: 32,
p: 1,
r: 8,
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
}),
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
},
name: Some("Test".to_owned()),
meta: None,
};
let serialized = serde_json::to_string(&file).unwrap();
println!("{}", serialized);
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(file, deserialized);
}
}

View File

@ -1,43 +0,0 @@
// Copyright 2015-2020 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/>.
//! Contract interface specification.
mod bytes;
mod cipher;
mod crypto;
mod error;
mod hash;
mod id;
mod kdf;
mod key_file;
mod presale;
mod vault_file;
mod vault_key_file;
mod version;
pub use self::bytes::Bytes;
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
pub use self::crypto::{Crypto, CipherText};
pub use self::error::Error;
pub use self::hash::{H128, H160, H256};
pub use self::id::Uuid;
pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams};
pub use self::key_file::{KeyFile, OpaqueKeyFile};
pub use self::presale::{PresaleWallet, Encseed};
pub use self::vault_file::VaultFile;
pub use self::vault_key_file::{VaultKeyFile, VaultKeyMeta, insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
pub use self::version::Version;

View File

@ -1,94 +0,0 @@
// Copyright 2015-2020 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/>.
use std::io::{Read, Write};
use serde_json;
use super::Crypto;
/// Vault meta file
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct VaultFile {
/// Vault password, encrypted with vault password
pub crypto: Crypto,
/// Vault metadata string
pub meta: Option<String>,
}
impl VaultFile {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
serde_json::from_reader(reader)
}
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write {
serde_json::to_writer(writer, self)
}
}
#[cfg(test)]
mod test {
use serde_json;
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
#[test]
fn to_and_from_json() {
let file = VaultFile {
crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "0155e3690be19fbfbecabcd440aa284b".into(),
}),
ciphertext: "4d6938a1f49b7782".into(),
kdf: Kdf::Pbkdf2(Pbkdf2 {
c: 1024,
dklen: 32,
prf: Prf::HmacSha256,
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
}),
mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(),
},
meta: Some("{}".into()),
};
let serialized = serde_json::to_string(&file).unwrap();
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(file, deserialized);
}
#[test]
fn to_and_from_json_no_meta() {
let file = VaultFile {
crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "0155e3690be19fbfbecabcd440aa284b".into(),
}),
ciphertext: "4d6938a1f49b7782".into(),
kdf: Kdf::Pbkdf2(Pbkdf2 {
c: 1024,
dklen: 32,
prf: Prf::HmacSha256,
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
}),
mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(),
},
meta: None,
};
let serialized = serde_json::to_string(&file).unwrap();
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(file, deserialized);
}
}

View File

@ -1,172 +0,0 @@
// Copyright 2015-2020 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/>.
use std::io::{Read, Write};
use serde::de::Error;
use serde_json;
use serde_json::value::Value;
use serde_json::error;
use super::{Uuid, Version, Crypto, H160};
/// Meta key name for vault field
const VAULT_NAME_META_KEY: &'static str = "vault";
/// Key file as stored in vaults
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct VaultKeyFile {
/// Key id
pub id: Uuid,
/// Key version
pub version: Version,
/// Secret, encrypted with account password
pub crypto: Crypto,
/// Serialized `VaultKeyMeta`, encrypted with vault password
pub metacrypto: Crypto,
}
/// Data, stored in `VaultKeyFile::metacrypto`
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct VaultKeyMeta {
/// Key address
pub address: H160,
/// Key name
pub name: Option<String>,
/// Key metadata
pub meta: Option<String>,
}
/// Insert vault name to the JSON meta field
pub fn insert_vault_name_to_json_meta(meta: &str, vault_name: &str) -> Result<String, error::Error> {
let mut meta = if meta.is_empty() {
Value::Object(serde_json::Map::new())
} else {
serde_json::from_str(meta)?
};
if let Some(meta_obj) = meta.as_object_mut() {
meta_obj.insert(VAULT_NAME_META_KEY.to_owned(), Value::String(vault_name.to_owned()));
serde_json::to_string(meta_obj)
} else {
Err(error::Error::custom("Meta is expected to be a serialized JSON object"))
}
}
/// Remove vault name from the JSON meta field
pub fn remove_vault_name_from_json_meta(meta: &str) -> Result<String, error::Error> {
let mut meta = if meta.is_empty() {
Value::Object(serde_json::Map::new())
} else {
serde_json::from_str(meta)?
};
if let Some(meta_obj) = meta.as_object_mut() {
meta_obj.remove(VAULT_NAME_META_KEY);
serde_json::to_string(meta_obj)
} else {
Err(error::Error::custom("Meta is expected to be a serialized JSON object"))
}
}
impl VaultKeyFile {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
serde_json::from_reader(reader)
}
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write {
serde_json::to_writer(writer, self)
}
}
impl VaultKeyMeta {
pub fn load(bytes: &[u8]) -> Result<Self, serde_json::Error> {
serde_json::from_slice(&bytes)
}
pub fn write(&self) -> Result<Vec<u8>, serde_json::Error> {
let s = serde_json::to_string(self)?;
Ok(s.as_bytes().into())
}
}
#[cfg(test)]
mod test {
use serde_json;
use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf,
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
#[test]
fn to_and_from_json() {
let file = VaultKeyFile {
id: "08d82c39-88e3-7a71-6abb-89c8f36c3ceb".into(),
version: Version::V3,
crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "fecb968bbc8c7e608a89ebcfe53a41d0".into(),
}),
ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
kdf: Kdf::Pbkdf2(Pbkdf2 {
c: 10240,
dklen: 32,
prf: Prf::HmacSha256,
salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
}),
mac: "7c7c3daafb24cf11eb3079dfb9064a11e92f309a0ee1dd676486bab119e686b7".into(),
},
metacrypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "9c353fb3f894fc05946843616c26bb3f".into(),
}),
ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
kdf: Kdf::Pbkdf2(Pbkdf2 {
c: 10240,
dklen: 32,
prf: Prf::HmacSha256,
salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
}),
mac: "b7413946bfe459d2801268dc331c04b3a84d92be11ef4dd9a507f895e8d9b5bd".into(),
}
};
let serialized = serde_json::to_string(&file).unwrap();
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(file, deserialized);
}
#[test]
fn vault_name_inserted_to_json_meta() {
assert_eq!(insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(), r#"{"vault":"MyVault"}"#);
assert_eq!(insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(), r#"{"tags":["kalabala"],"vault":"MyVault"}"#);
}
#[test]
fn vault_name_not_inserted_to_json_meta() {
assert!(insert_vault_name_to_json_meta(r#"///3533"#, "MyVault").is_err());
assert!(insert_vault_name_to_json_meta(r#""string""#, "MyVault").is_err());
}
#[test]
fn vault_name_removed_from_json_meta() {
assert_eq!(remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(), r#"{}"#);
assert_eq!(remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(), r#"{"tags":["kalabala"]}"#);
}
#[test]
fn vault_name_not_removed_from_json_meta() {
assert!(remove_vault_name_from_json_meta(r#"///3533"#).is_err());
assert!(remove_vault_name_from_json_meta(r#""string""#).is_err());
}
}

View File

@ -1,58 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Error as SerdeError, Visitor};
use super::Error;
#[derive(Debug, PartialEq)]
pub enum Version {
V3,
}
impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match *self {
Version::V3 => serializer.serialize_u64(3)
}
}
}
impl<'a> Deserialize<'a> for Version {
fn deserialize<D>(deserializer: D) -> Result<Version, D::Error>
where D: Deserializer<'a> {
deserializer.deserialize_any(VersionVisitor)
}
}
struct VersionVisitor;
impl<'a> Visitor<'a> for VersionVisitor {
type Value = Version;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid key version identifier")
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> where E: SerdeError {
match value {
3 => Ok(Version::V3),
_ => Err(SerdeError::custom(Error::UnsupportedVersion))
}
}
}

View File

@ -1,100 +0,0 @@
// Copyright 2015-2020 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/>.
//! Ethereum key-management.
#![warn(missing_docs)]
extern crate dir;
extern crate itertools;
extern crate libc;
extern crate parking_lot;
extern crate rand;
extern crate rustc_hex;
extern crate serde;
extern crate serde_json;
extern crate smallvec;
extern crate time;
extern crate tiny_keccak;
extern crate tempdir;
extern crate parity_crypto as crypto;
extern crate ethereum_types;
extern crate ethkey as ethkey;
extern crate parity_wordlist;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
#[macro_use]
extern crate matches;
pub mod accounts_dir;
mod account;
mod json;
mod error;
mod ethstore;
mod import;
mod presale;
mod random;
mod secret_store;
pub use self::account::{SafeAccount, Crypto};
pub use self::error::Error;
pub use self::ethstore::{EthStore, EthMultiStore};
pub use self::import::{import_account, import_accounts, read_geth_accounts};
pub use self::json::OpaqueKeyFile as KeyFile;
pub use self::presale::PresaleWallet;
pub use self::secret_store::{
SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore,
Derivation, IndexDerivation,
};
pub use self::random::random_string;
pub use self::parity_wordlist::random_phrase;
/// An opaque wrapper for secret.
pub struct OpaqueSecret(crypto::publickey::Secret);
// Additional converters for Address
use crypto::publickey::Address;
impl Into<json::H160> for Address {
fn into(self) -> json::H160 {
let a: [u8; 20] = self.into();
From::from(a)
}
}
impl From<json::H160> for Address {
fn from(json: json::H160) -> Self {
let a: [u8; 20] = json.into();
From::from(a)
}
}
impl<'a> From<&'a json::H160> for Address {
fn from(json: &'a json::H160) -> Self {
let mut a = [0u8; 20];
a.copy_from_slice(json);
From::from(a)
}
}

View File

@ -1,100 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fs;
use std::path::Path;
use json;
use crypto::publickey::{Address, Secret, KeyPair};
use ethkey::Password;
use crypto::{Keccak256, pbkdf2};
use {crypto, Error};
/// Pre-sale wallet.
pub struct PresaleWallet {
iv: [u8; 16],
ciphertext: Vec<u8>,
address: Address,
}
impl From<json::PresaleWallet> for PresaleWallet {
fn from(wallet: json::PresaleWallet) -> Self {
let mut iv = [0u8; 16];
iv.copy_from_slice(&wallet.encseed[..16]);
let mut ciphertext = vec![];
ciphertext.extend_from_slice(&wallet.encseed[16..]);
PresaleWallet {
iv: iv,
ciphertext: ciphertext,
address: Address::from(wallet.address),
}
}
}
impl PresaleWallet {
/// Open a pre-sale wallet.
pub fn open<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
let file = fs::File::open(path)?;
let presale = json::PresaleWallet::load(file)
.map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?;
Ok(PresaleWallet::from(presale))
}
/// Decrypt the wallet.
pub fn decrypt(&self, password: &Password) -> Result<KeyPair, Error> {
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 mut key = vec![0; self.ciphertext.len()];
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
.map_err(|_| Error::InvalidPassword)?;
let unpadded = &key[..len];
let secret = Secret::import_key(&unpadded.keccak256())?;
if let Ok(kp) = KeyPair::from_secret(secret) {
if kp.address() == self.address {
return Ok(kp)
}
}
Err(Error::InvalidPassword)
}
}
#[cfg(test)]
mod tests {
use super::PresaleWallet;
use json;
#[test]
fn test() {
let json = r#"
{
"encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066",
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
"email": "123@gmail.com",
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
} "#;
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
let wallet = PresaleWallet::from(wallet);
assert!(wallet.decrypt(&"123".into()).is_ok());
assert!(wallet.decrypt(&"124".into()).is_err());
}
}

View File

@ -1,45 +0,0 @@
// Copyright 2015-2020 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/>.
use rand::{Rng, RngCore, rngs::OsRng, distributions::Alphanumeric};
pub trait Random {
fn random() -> Self where Self: Sized;
}
impl Random for [u8; 16] {
fn random() -> Self {
let mut result = [0u8; 16];
let mut rng = OsRng;
rng.fill_bytes(&mut result);
result
}
}
impl Random for [u8; 32] {
fn random() -> Self {
let mut result = [0u8; 32];
let mut rng = OsRng;
rng.fill_bytes(&mut result);
result
}
}
/// Generate a random string of given length.
pub fn random_string(length: usize) -> String {
let rng = OsRng;
rng.sample_iter(&Alphanumeric).take(length).collect()
}

View File

@ -1,191 +0,0 @@
// Copyright 2015-2020 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/>.
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::cmp::Ordering;
use crypto::publickey::{Address, Message, Signature, Secret, Public};
use ethkey::Password;
use Error;
use json::{Uuid, OpaqueKeyFile};
use ethereum_types::H256;
use OpaqueSecret;
/// Key directory reference
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SecretVaultRef {
/// Reference to key in root directory
Root,
/// Referenc to key in specific vault
Vault(String),
}
/// Stored account reference
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
pub struct StoreAccountRef {
/// Account address
pub address: Address,
/// Vault reference
pub vault: SecretVaultRef,
}
impl PartialOrd for StoreAccountRef {
fn partial_cmp(&self, other: &StoreAccountRef) -> Option<Ordering> {
Some(self.address.cmp(&other.address).then_with(|| self.vault.cmp(&other.vault)))
}
}
impl ::std::borrow::Borrow<Address> for StoreAccountRef {
fn borrow(&self) -> &Address {
&self.address
}
}
/// Simple Secret Store API
pub trait SimpleSecretStore: Send + Sync {
/// Inserts new accounts to the store (or vault) with given password.
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result<StoreAccountRef, Error>;
/// Inserts new derived account to the store (or vault) with given password.
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<StoreAccountRef, Error>;
/// Changes accounts password.
fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error>;
/// Exports key details for account.
fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueKeyFile, Error>;
/// Entirely removes account from the store and underlying storage.
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>;
/// Generates new derived account.
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<Address, Error>;
/// Sign a message with given account.
fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result<Signature, Error>;
/// Sign a message with derived account.
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) -> Result<Signature, Error>;
/// Decrypt a messages with given account.
fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
/// Agree on shared key.
fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result<Secret, Error>;
/// Returns all accounts in this secret store.
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
/// Get reference to some account with given address.
/// This method could be removed if we will guarantee that there is max(1) account for given address.
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error>;
/// Create new vault with given password
fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
/// Open vault with given password
fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
/// Close vault
fn close_vault(&self, name: &str) -> Result<(), Error>;
/// List all vaults
fn list_vaults(&self) -> Result<Vec<String>, Error>;
/// List all currently opened vaults
fn list_opened_vaults(&self) -> Result<Vec<String>, Error>;
/// Change vault password
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>;
/// Cnage account' vault
fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result<StoreAccountRef, Error>;
/// Get vault metadata string.
fn get_vault_meta(&self, name: &str) -> Result<String, Error>;
/// Set vault metadata string.
fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error>;
}
/// Secret Store API
pub trait SecretStore: SimpleSecretStore {
/// Returns a raw opaque Secret that can be later used to sign a message.
fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueSecret, Error>;
/// Signs a message with raw secret.
fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result<Signature, Error> {
Ok(crypto::publickey::sign(&secret.0, message)?)
}
/// Imports presale wallet
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result<StoreAccountRef, Error>;
/// Imports existing JSON wallet
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error>;
/// Copies account between stores and vaults.
fn copy_account(&self, new_store: &dyn SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>;
/// Checks if password matches given account.
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>;
/// Returns a public key for given account.
fn public(&self, account: &StoreAccountRef, password: &Password) -> Result<Public, Error>;
/// Returns uuid of an account.
fn uuid(&self, account: &StoreAccountRef) -> Result<Uuid, Error>;
/// Returns account's name.
fn name(&self, account: &StoreAccountRef) -> Result<String, Error>;
/// Returns account's metadata.
fn meta(&self, account: &StoreAccountRef) -> Result<String, Error>;
/// Modifies account metadata.
fn set_name(&self, account: &StoreAccountRef, name: String) -> Result<(), Error>;
/// Modifies account name.
fn set_meta(&self, account: &StoreAccountRef, meta: String) -> Result<(), Error>;
/// Returns local path of the store.
fn local_path(&self) -> PathBuf;
/// Lists all found geth accounts.
fn list_geth_accounts(&self, testnet: bool) -> Vec<Address>;
/// Imports geth accounts to the store/vault.
fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec<Address>, testnet: bool) -> Result<Vec<StoreAccountRef>, Error>;
}
impl StoreAccountRef {
/// Create reference to root account with given address
pub fn root(address: Address) -> Self {
StoreAccountRef::new(SecretVaultRef::Root, address)
}
/// Create reference to vault account with given address
pub fn vault(vault_name: &str, address: Address) -> Self {
StoreAccountRef::new(SecretVaultRef::Vault(vault_name.to_owned()), address)
}
/// Create new account reference
pub fn new(vault_ref: SecretVaultRef, address: Address) -> Self {
StoreAccountRef {
vault: vault_ref,
address: address,
}
}
}
impl Hash for StoreAccountRef {
fn hash<H: Hasher>(&self, state: &mut H) {
self.address.hash(state);
}
}
/// Node in hierarchical derivation.
pub struct IndexDerivation {
/// Node is soft (allows proof of parent from parent node).
pub soft: bool,
/// Index sequence of the node.
pub index: u32,
}
/// Derivation scheme for keys
pub enum Derivation {
/// Hierarchical derivation
Hierarchical(Vec<IndexDerivation>),
/// Hash derivation, soft.
SoftHash(H256),
/// Hash derivation, hard.
HardHash(H256),
}

View File

@ -1,158 +0,0 @@
// Copyright 2015-2020 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/>.
extern crate rand;
extern crate ethstore;
extern crate ethereum_types;
extern crate parity_crypto;
mod util;
use ethstore::{EthStore, SimpleSecretStore, SecretVaultRef, StoreAccountRef};
use parity_crypto::publickey::{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() {
let dir = TransientDir::create().unwrap();
let _ = EthStore::open(Box::new(dir)).unwrap();
}
#[test]
#[should_panic]
fn secret_store_open_not_existing() {
let dir = TransientDir::open();
let _ = EthStore::open(Box::new(dir)).unwrap();
}
fn random_secret() -> Secret {
Random.generate().unwrap().secret().clone()
}
#[test]
fn secret_store_create_account() {
let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts().unwrap().len(), 0);
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
assert_eq!(store.accounts().unwrap().len(), 1);
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
assert_eq!(store.accounts().unwrap().len(), 2);
}
#[test]
fn secret_store_sign() {
let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap();
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
let accounts = store.accounts().unwrap();
assert_eq!(accounts.len(), 1);
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok());
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_err());
}
#[test]
fn secret_store_change_password() {
let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap();
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
let accounts = store.accounts().unwrap();
assert_eq!(accounts.len(), 1);
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok());
assert!(store.change_password(&accounts[0], &"".into(), &"1".into()).is_ok());
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_err());
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_ok());
}
#[test]
fn secret_store_remove_account() {
let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap();
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
let accounts = store.accounts().unwrap();
assert_eq!(accounts.len(), 1);
assert!(store.remove_account(&accounts[0], &"".into()).is_ok());
assert_eq!(store.accounts().unwrap().len(), 0);
assert!(store.remove_account(&accounts[0], &"".into()).is_err());
}
fn test_path() -> &'static str {
match ::std::fs::metadata("ethstore") {
Ok(_) => "ethstore/tests/res/geth_keystore",
Err(_) => "tests/res/geth_keystore",
}
}
fn pat_path() -> &'static str {
match ::std::fs::metadata("ethstore") {
Ok(_) => "ethstore/tests/res/pat",
Err(_) => "tests/res/pat",
}
}
fn ciphertext_path() -> &'static str {
match ::std::fs::metadata("ethstore") {
Ok(_) => "ethstore/tests/res/ciphertext",
Err(_) => "tests/res/ciphertext",
}
}
#[test]
fn secret_store_laod_geth_files() {
let dir = RootDiskDirectory::at(test_path());
let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts().unwrap(), vec![
StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()),
StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()),
StoreAccountRef::root(Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap()),
]);
}
#[test]
fn secret_store_load_pat_files() {
let dir = RootDiskDirectory::at(pat_path());
let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts().unwrap(), vec![
StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()),
StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()),
]);
}
#[test]
fn test_decrypting_files_with_short_ciphertext() {
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap();
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap();
let dir = RootDiskDirectory::at(ciphertext_path());
let store = EthStore::open(Box::new(dir)).unwrap();
let accounts = store.accounts().unwrap();
assert_eq!(accounts, vec![
StoreAccountRef::root(Address::from_str("31e9d1e6d844bd3a536800ef8d8be6a9975db509").unwrap()),
StoreAccountRef::root(Address::from_str("d1e64e5480bfaf733ba7d48712decb8227797a4e").unwrap()),
]);
let message = Default::default();
let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap();
let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap();
assert!(verify_address(&accounts[0].address, &s1, &message).unwrap());
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
}

View File

@ -1,81 +0,0 @@
// Copyright 2015-2020 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/>.
use std::path::PathBuf;
use std::{env, fs};
use rand::{RngCore, rngs::OsRng};
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
use ethstore::{Error, SafeAccount};
pub fn random_dir() -> PathBuf {
let mut rng = OsRng;
let mut dir = env::temp_dir();
dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64()));
dir
}
pub struct TransientDir {
dir: RootDiskDirectory,
path: PathBuf,
}
impl TransientDir {
pub fn create() -> Result<Self, Error> {
let path = random_dir();
let result = TransientDir {
dir: RootDiskDirectory::create(&path)?,
path: path,
};
Ok(result)
}
pub fn open() -> Self {
let path = random_dir();
TransientDir {
dir: RootDiskDirectory::at(&path),
path: path,
}
}
}
impl Drop for TransientDir {
fn drop(&mut self) {
fs::remove_dir_all(&self.path).expect("Expected to remove temp dir");
}
}
impl KeyDirectory for TransientDir {
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
self.dir.load()
}
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
self.dir.update(account)
}
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
self.dir.insert(account)
}
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
self.dir.remove(account)
}
fn unique_repr(&self) -> Result<u64, Error> {
self.dir.unique_repr()
}
}

View File

@ -1,74 +0,0 @@
// Copyright 2015-2020 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/>.
//! Account Metadata
use std::{
collections::HashMap,
time::Instant,
};
use parity_crypto::publickey::Address;
use ethkey::Password;
use serde_derive::{Serialize, Deserialize};
use serde_json;
/// Type of unlock.
#[derive(Clone, PartialEq)]
pub enum Unlock {
/// If account is unlocked temporarily, it should be locked after first usage.
OneTime,
/// Account unlocked permanently can always sign message.
/// Use with caution.
Perm,
/// Account unlocked with a timeout
Timed(Instant),
}
/// Data associated with account.
#[derive(Clone)]
pub struct AccountData {
pub unlock: Unlock,
pub password: Password,
}
/// Collected account metadata
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AccountMeta {
/// The name of the account.
pub name: String,
/// The rest of the metadata of the account.
pub meta: String,
/// The 128-bit Uuid of the account, if it has one (brain-wallets don't).
pub uuid: Option<String>,
}
impl AccountMeta {
/// Read a hash map of Address -> AccountMeta
pub fn read<R>(reader: R) -> Result<HashMap<Address, Self>, serde_json::Error> where
R: ::std::io::Read,
{
serde_json::from_reader(reader)
}
/// Write a hash map of Address -> AccountMeta
pub fn write<W>(m: &HashMap<Address, Self>, writer: &mut W) -> Result<(), serde_json::Error> where
W: ::std::io::Write,
{
serde_json::to_writer(writer, m)
}
}

View File

@ -1,46 +0,0 @@
// Copyright 2015-2020 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/>.
use std::fmt;
use ethstore::{Error as SSError};
/// Signing error
#[derive(Debug)]
pub enum SignError {
/// Account is not unlocked
NotUnlocked,
/// Account does not exist.
NotFound,
/// Low-level error from store
SStore(SSError),
}
impl fmt::Display for SignError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
SignError::NotUnlocked => write!(f, "Account is locked"),
SignError::NotFound => write!(f, "Account does not exist"),
SignError::SStore(ref e) => write!(f, "{}", e),
}
}
}
impl From<SSError> for SignError {
fn from(e: SSError) -> Self {
SignError::SStore(e)
}
}

View File

@ -1,643 +0,0 @@
// Copyright 2015-2020 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/>.
#![warn(missing_docs)]
//! Account management.
mod account_data;
mod error;
mod stores;
use self::account_data::{Unlock, AccountData};
use self::stores::AddressBook;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use ethkey::Password;
use parity_crypto::publickey::{Address, Message, Public, Secret, Random, Generator, Signature};
use ethstore::accounts_dir::MemoryDirectory;
use ethstore::{
SimpleSecretStore, SecretStore, EthStore, EthMultiStore,
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
};
use log::warn;
use parking_lot::RwLock;
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
pub use self::account_data::AccountMeta;
pub use self::error::SignError;
type AccountToken = Password;
/// Account management settings.
#[derive(Debug, Default)]
pub struct AccountProviderSettings {
/// Store raw account secret when unlocking the account permanently.
pub unlock_keep_secret: bool,
/// Disallowed accounts.
pub blacklisted_accounts: Vec<Address>,
}
/// Account management.
/// Responsible for unlocking accounts.
pub struct AccountProvider {
/// For performance reasons some methods can re-use unlocked secrets.
unlocked_secrets: RwLock<HashMap<StoreAccountRef, OpaqueSecret>>,
/// Unlocked account data.
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
/// Address book.
address_book: RwLock<AddressBook>,
/// Accounts on disk
sstore: Box<dyn SecretStore>,
/// Accounts unlocked with rolling tokens
transient_sstore: EthMultiStore,
/// When unlocking account permanently we additionally keep a raw secret in memory
/// to increase the performance of transaction signing.
unlock_keep_secret: bool,
/// Disallowed accounts.
blacklisted_accounts: Vec<Address>,
}
fn transient_sstore() -> EthMultiStore {
EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")
}
impl AccountProvider {
/// Creates new account provider.
pub fn new(sstore: Box<dyn SecretStore>, settings: AccountProviderSettings) -> Self {
if let Ok(accounts) = sstore.accounts() {
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) {
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
account.address);
}
}
// Remove blacklisted accounts from address book.
let mut address_book = AddressBook::new(&sstore.local_path());
for addr in &settings.blacklisted_accounts {
address_book.remove(*addr);
}
AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
address_book: RwLock::new(address_book),
sstore,
transient_sstore: transient_sstore(),
unlock_keep_secret: settings.unlock_keep_secret,
blacklisted_accounts: settings.blacklisted_accounts,
}
}
/// Creates not disk backed provider.
pub fn transient_provider() -> Self {
AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
address_book: RwLock::new(AddressBook::transient()),
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
transient_sstore: transient_sstore(),
unlock_keep_secret: false,
blacklisted_accounts: vec![],
}
}
/// Creates new random account.
pub fn new_account(&self, password: &Password) -> Result<Address, Error> {
self.new_account_and_public(password).map(|d| d.0)
}
/// Creates new random account and returns address and public key
pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> {
let acc = Random.generate().expect("secp context has generation capabilities; qed");
let public = acc.public().clone();
let secret = acc.secret().clone();
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
Ok((account.address, public))
}
/// Inserts new account into underlying store.
/// Does not unlock account!
pub fn insert_account(&self, secret: Secret, password: &Password) -> Result<Address, Error> {
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
if self.blacklisted_accounts.contains(&account.address) {
self.sstore.remove_account(&account, password)?;
return Err(Error::InvalidAccount.into());
}
Ok(account.address)
}
/// Generates new derived account based on the existing one
/// If password is not provided, account must be unlocked
/// New account will be created with the same password (if save: true)
pub fn derive_account(&self, address: &Address, password: Option<Password>, derivation: Derivation, save: bool)
-> Result<Address, SignError>
{
let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(
if save { self.sstore.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?.address }
else { self.sstore.generate_derived(&account, &password, derivation)? }
)
}
/// Import a new presale wallet.
pub fn import_presale(&self, presale_json: &[u8], password: &Password) -> Result<Address, Error> {
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?;
Ok(Address::from(account.address).into())
}
/// Import a new wallet.
pub fn import_wallet(&self, json: &[u8], password: &Password, gen_id: bool) -> Result<Address, Error> {
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
if self.blacklisted_accounts.contains(&account.address) {
self.sstore.remove_account(&account, password)?;
return Err(Error::InvalidAccount.into());
}
Ok(Address::from(account.address).into())
}
/// Checks whether an account with a given address is present.
pub fn has_account(&self, address: Address) -> bool {
self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address)
}
/// Returns addresses of all accounts.
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
let accounts = self.sstore.accounts()?;
Ok(accounts
.into_iter()
.map(|a| a.address)
.filter(|address| !self.blacklisted_accounts.contains(address))
.collect()
)
}
/// Returns the address of default account.
pub fn default_account(&self) -> Result<Address, Error> {
Ok(self.accounts()?.first().cloned().unwrap_or_default())
}
/// Returns each address along with metadata.
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
self.address_book.read().get()
}
/// Returns each address along with metadata.
pub fn set_address_name(&self, account: Address, name: String) {
self.address_book.write().set_name(account, name)
}
/// Returns each address along with metadata.
pub fn set_address_meta(&self, account: Address, meta: String) {
self.address_book.write().set_meta(account, meta)
}
/// Removes and address from the address book
pub fn remove_address(&self, addr: Address) {
self.address_book.write().remove(addr)
}
/// Returns each account along with name and meta.
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
let r = self.sstore.accounts()?
.into_iter()
.filter(|a| !self.blacklisted_accounts.contains(&a.address))
.map(|a| (a.address.clone(), self.account_meta(a.address).ok().unwrap_or_default()))
.collect();
Ok(r)
}
/// Returns each account along with name and meta.
pub fn account_meta(&self, address: Address) -> Result<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
})
}
/// Returns account public key.
pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> {
self.sstore.public(&self.sstore.account_ref(&address)?, password)
}
/// Returns each account along with name and meta.
pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> {
self.sstore.set_name(&self.sstore.account_ref(&address)?, name)?;
Ok(())
}
/// Returns each account along with name and meta.
pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> {
self.sstore.set_meta(&self.sstore.account_ref(&address)?, meta)?;
Ok(())
}
/// Returns `true` if the password for `account` is `password`. `false` if not.
pub fn test_password(&self, address: &Address, password: &Password) -> Result<bool, Error> {
self.sstore.test_password(&self.sstore.account_ref(&address)?, password)
.map_err(Into::into)
}
/// Permanently removes an account.
pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> {
self.sstore.remove_account(&self.sstore.account_ref(&address)?, &password)?;
Ok(())
}
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
pub fn change_password(&self, address: &Address, password: Password, new_password: Password) -> Result<(), Error> {
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
}
/// Exports an account for given address.
pub fn export_account(&self, address: &Address, password: Password) -> Result<KeyFile, Error> {
self.sstore.export_account(&self.sstore.account_ref(address)?, &password)
}
/// Helper method used for unlocking accounts.
fn unlock_account(&self, address: Address, password: Password, unlock: Unlock) -> Result<(), Error> {
let account = self.sstore.account_ref(&address)?;
// check if account is already unlocked permanently, if it is, do nothing
let mut unlocked = self.unlocked.write();
if let Some(data) = unlocked.get(&account) {
if let Unlock::Perm = data.unlock {
return Ok(())
}
}
if self.unlock_keep_secret && unlock == Unlock::Perm {
// verify password and get the secret
let secret = self.sstore.raw_secret(&account, &password)?;
self.unlocked_secrets.write().insert(account.clone(), secret);
} else {
// verify password by signing dump message
// result may be discarded
let _ = self.sstore.sign(&account, &password, &Default::default())?;
}
let data = AccountData { unlock, password };
unlocked.insert(account, data);
Ok(())
}
fn password(&self, account: &StoreAccountRef) -> Result<Password, SignError> {
let mut unlocked = self.unlocked.write();
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
if let Unlock::OneTime = data.unlock {
unlocked.remove(account).expect("data exists: so key must exist: qed");
}
if let Unlock::Timed(ref end) = data.unlock {
if Instant::now() > *end {
unlocked.remove(account).expect("data exists: so key must exist: qed");
return Err(SignError::NotUnlocked);
}
}
Ok(data.password)
}
/// Unlocks account permanently.
pub fn unlock_account_permanently(&self, account: Address, password: Password) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::Perm)
}
/// Unlocks account temporarily (for one signing).
pub fn unlock_account_temporarily(&self, account: Address, password: Password) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::OneTime)
}
/// Unlocks account temporarily with a timeout.
pub fn unlock_account_timed(&self, account: Address, password: Password, duration: Duration) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration))
}
/// Checks if given account is unlocked
pub fn is_unlocked(&self, address: &Address) -> bool {
let unlocked = self.unlocked.read();
let unlocked_secrets = self.unlocked_secrets.read();
self.sstore.account_ref(address)
.map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some())
.unwrap_or(false)
}
/// Checks if given account is unlocked permanently
pub fn is_unlocked_permanently(&self, address: &Address) -> bool {
let unlocked = self.unlocked.read();
self.sstore.account_ref(address)
.map(|r| unlocked.get(&r).map_or(false, |account| account.unlock == Unlock::Perm))
.unwrap_or(false)
}
/// Signs the message. If password is not provided the account must be unlocked.
pub fn sign(&self, address: Address, password: Option<Password>, message: Message) -> Result<Signature, SignError> {
let account = self.sstore.account_ref(&address)?;
match self.unlocked_secrets.read().get(&account) {
Some(secret) => {
Ok(self.sstore.sign_with_secret(&secret, &message)?)
},
None => {
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.sign(&account, &password, &message)?)
}
}
}
/// Signs message using the derived secret. If password is not provided the account must be unlocked.
pub fn sign_derived(&self, address: &Address, password: Option<Password>, derivation: Derivation, message: Message)
-> Result<Signature, SignError>
{
let account = self.sstore.account_ref(address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.sign_derived(&account, &password, derivation, &message)?)
}
/// Signs given message with supplied token. Returns a token to use in next signing within this session.
pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), SignError> {
let account = self.sstore.account_ref(&address)?;
let is_std_password = self.sstore.test_password(&account, &token)?;
let new_token = Password::from(random_string(16));
let signature = if is_std_password {
// Insert to transient store
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
// sign
self.sstore.sign(&account, &token, &message)?
} else {
// check transient store
self.transient_sstore.change_password(&account, &token, &new_token)?;
// and sign
self.transient_sstore.sign(&account, &new_token, &message)?
};
Ok((signature, new_token))
}
/// Decrypts a message with given token. Returns a token to use in next operation for this account.
pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
-> Result<(Vec<u8>, AccountToken), SignError>
{
let account = self.sstore.account_ref(&address)?;
let is_std_password = self.sstore.test_password(&account, &token)?;
let new_token = Password::from(random_string(16));
let message = if is_std_password {
// Insert to transient store
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
// decrypt
self.sstore.decrypt(&account, &token, shared_mac, message)?
} else {
// check transient store
self.transient_sstore.change_password(&account, &token, &new_token)?;
// and decrypt
self.transient_sstore.decrypt(&account, &token, shared_mac, message)?
};
Ok((message, new_token))
}
/// Decrypts a message. If password is not provided the account must be unlocked.
pub fn decrypt(&self, address: Address, password: Option<Password>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, SignError> {
let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
}
/// Agree on shared key.
pub fn agree(&self, address: Address, password: Option<Password>, other_public: &Public) -> Result<Secret, SignError> {
let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.agree(&account, &password, other_public)?)
}
/// Returns the underlying `SecretStore` reference if one exists.
pub fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect()
}
/// Returns the underlying `SecretStore` reference if one exists.
pub fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
self.sstore.import_geth_accounts(SecretVaultRef::Root, desired, testnet)
.map(|a| a.into_iter().map(|a| a.address).collect())
.map_err(Into::into)
}
/// Create new vault.
pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
self.sstore.create_vault(name, password)
.map_err(Into::into)
}
/// Open existing vault.
pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
self.sstore.open_vault(name, password)
.map_err(Into::into)
}
/// Close previously opened vault.
pub fn close_vault(&self, name: &str) -> Result<(), Error> {
self.sstore.close_vault(name)
.map_err(Into::into)
}
/// List all vaults
pub fn list_vaults(&self) -> Result<Vec<String>, Error> {
self.sstore.list_vaults()
.map_err(Into::into)
}
/// List all currently opened vaults
pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
self.sstore.list_opened_vaults()
.map_err(Into::into)
}
/// Change vault password.
pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
self.sstore.change_vault_password(name, new_password)
.map_err(Into::into)
}
/// Change vault of the given address.
pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> {
let new_vault_ref = if new_vault.is_empty() { SecretVaultRef::Root } else { SecretVaultRef::Vault(new_vault.to_owned()) };
let old_account_ref = self.sstore.account_ref(&address)?;
self.sstore.change_account_vault(new_vault_ref, old_account_ref)
.map_err(Into::into)
.map(|_| ())
}
/// Get vault metadata string.
pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
self.sstore.get_vault_meta(name)
.map_err(Into::into)
}
/// Set vault metadata string.
pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
self.sstore.set_vault_meta(name, meta)
.map_err(Into::into)
}
}
#[cfg(test)]
mod tests {
use super::{AccountProvider, Unlock};
use std::time::{Duration, Instant};
use parity_crypto::publickey::{Generator, Random, Address};
use ethstore::{StoreAccountRef, Derivation};
use ethereum_types::H256;
#[test]
fn unlock_account_temp() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
}
#[test]
fn derived_account_nosave() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
let derived_addr = ap.derive_account(
&kp.address(),
None,
Derivation::SoftHash(H256::from_low_u64_be(999)),
false,
).expect("Derivation should not fail");
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_err(),
"There should be an error because account is not supposed to be saved");
}
#[test]
fn derived_account_save() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
let derived_addr = ap.derive_account(
&kp.address(),
None,
Derivation::SoftHash(H256::from_low_u64_be(999)),
true,
).expect("Derivation should not fail");
assert!(ap.unlock_account_permanently(derived_addr, "base_wrong".into()).is_err(),
"There should be an error because password is invalid");
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_ok(),
"Should be ok because account is saved and password is valid");
}
#[test]
fn derived_account_sign() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
let derived_addr = ap.derive_account(
&kp.address(),
None,
Derivation::SoftHash(H256::from_low_u64_be(1999)),
true,
).expect("Derivation should not fail");
ap.unlock_account_permanently(derived_addr, "base".into())
.expect("Should be ok because account is saved and password is valid");
let msg = Default::default();
let signed_msg1 = ap.sign(derived_addr, None, msg)
.expect("Signing with existing unlocked account should not fail");
let signed_msg2 = ap.sign_derived(
&kp.address(),
None,
Derivation::SoftHash(H256::from_low_u64_be(1999)),
msg,
).expect("Derived signing with existing unlocked account should not fail");
assert_eq!(signed_msg1, signed_msg2,
"Signed messages should match");
}
#[test]
fn unlock_account_perm() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
}
#[test]
fn unlock_account_timer() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)).is_err());
assert!(ap.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now());
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
}
#[test]
fn should_sign_and_return_token() {
// given
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
// when
let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap();
// then
ap.sign_with_token(kp.address(), token.clone(), Default::default())
.expect("First usage of token should be correct.");
assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail.");
}
#[test]
fn should_not_return_blacklisted_account() {
// given
let mut ap = AccountProvider::transient_provider();
let acc = ap.new_account(&"test".into()).unwrap();
ap.blacklisted_accounts = vec![acc];
// then
assert_eq!(ap.accounts_info().unwrap().keys().cloned().collect::<Vec<Address>>(), vec![]);
assert_eq!(ap.accounts().unwrap(), vec![]);
}
}

View File

@ -1,189 +0,0 @@
// Copyright 2015-2020 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/>.
//! Address Book Store
use std::{fs, fmt, hash, ops};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use parity_crypto::publickey::Address;
use log::{trace, warn};
use crate::AccountMeta;
/// Disk-backed map from Address to String. Uses JSON.
pub struct AddressBook {
cache: DiskMap<Address, AccountMeta>,
}
impl AddressBook {
/// Creates new address book at given directory.
pub fn new(path: &Path) -> Self {
let mut r = AddressBook {
cache: DiskMap::new(path, "address_book.json")
};
r.cache.revert(AccountMeta::read);
r
}
/// Creates transient address book (no changes are saved to disk).
pub fn transient() -> Self {
AddressBook {
cache: DiskMap::transient()
}
}
/// Get the address book.
pub fn get(&self) -> HashMap<Address, AccountMeta> {
self.cache.clone()
}
fn save(&self) {
self.cache.save(AccountMeta::write)
}
/// Sets new name for given address.
pub fn set_name(&mut self, a: Address, name: String) {
{
let x = self.cache.entry(a)
.or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None});
x.name = name;
}
self.save();
}
/// Sets new meta for given address.
pub fn set_meta(&mut self, a: Address, meta: String) {
{
let x = self.cache.entry(a)
.or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None});
x.meta = meta;
}
self.save();
}
/// Removes an entry
pub fn remove(&mut self, a: Address) {
self.cache.remove(&a);
self.save();
}
}
/// Disk-serializable HashMap
#[derive(Debug)]
struct DiskMap<K: hash::Hash + Eq, V> {
path: PathBuf,
cache: HashMap<K, V>,
transient: bool,
}
impl<K: hash::Hash + Eq, V> ops::Deref for DiskMap<K, V> {
type Target = HashMap<K, V>;
fn deref(&self) -> &Self::Target {
&self.cache
}
}
impl<K: hash::Hash + Eq, V> ops::DerefMut for DiskMap<K, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cache
}
}
impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
pub fn new(path: &Path, file_name: &str) -> Self {
let mut path = path.to_owned();
path.push(file_name);
trace!(target: "diskmap", "path={:?}", path);
DiskMap {
path: path,
cache: HashMap::new(),
transient: false,
}
}
pub fn transient() -> Self {
let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into());
map.transient = true;
map
}
fn revert<F, E>(&mut self, read: F) where
F: Fn(fs::File) -> Result<HashMap<K, V>, E>,
E: fmt::Display,
{
if self.transient { return; }
trace!(target: "diskmap", "revert {:?}", self.path);
let _ = fs::File::open(self.path.clone())
.map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e))
.and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map at: {:?} {}", self.path, e)))
.and_then(|m| {
self.cache = m;
Ok(())
});
}
fn save<F, E>(&self, write: F) where
F: Fn(&HashMap<K, V>, &mut fs::File) -> Result<(), E>,
E: fmt::Display,
{
if self.transient { return; }
trace!(target: "diskmap", "save {:?}", self.path);
let _ = fs::File::create(self.path.clone())
.map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing at: {:?} {}", self.path, e))
.and_then(|mut f| {
write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map at: {:?} {}", self.path, e))
});
}
}
#[cfg(test)]
mod tests {
use super::{AddressBook, Address};
use std::collections::HashMap;
use tempdir::TempDir;
use crate::account_data::AccountMeta;
#[test]
fn should_save_and_reload_address_book() {
let tempdir = TempDir::new("").unwrap();
let mut b = AddressBook::new(tempdir.path());
b.set_name(Address::from_low_u64_be(1), "One".to_owned());
b.set_meta(Address::from_low_u64_be(1), "{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<_, _>>());
}
#[test]
fn should_remove_address() {
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());
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<_, _>>());
}
}

View File

@ -5,5 +5,5 @@ version = "0.1.0"
authors = ["Marek Kotewicz <marek@parity.io>"]
[dependencies]
ethjson = { path = "../json" }
ethjson = { path = "../../crates/ethjson" }
serde_json = "1.0"

51
bin/chainspec/src/main.rs Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
extern crate ethjson;
extern crate serde_json;
use ethjson::spec::Spec;
use std::{env, fs, process};
fn quit(s: &str) -> ! {
println!("{}", s);
process::exit(1);
}
fn main() {
let mut args = env::args();
if args.len() != 2 {
quit(
"You need to specify chainspec.json\n\
\n\
./chainspec <chainspec.json>",
);
}
let path = args.nth(1).expect("args.len() == 2; qed");
let file = match fs::File::open(&path) {
Ok(file) => file,
Err(_) => quit(&format!("{} could not be opened", path)),
};
let spec: Result<Spec, _> = serde_json::from_reader(file);
if let Err(err) = spec {
quit(&format!("{} {}", path, err.to_string()));
}
println!("{} is valid", path);
}

View File

@ -7,10 +7,10 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
docopt = "1.0"
env_logger = "0.5"
ethkey = { path = "../" }
panic_hook = { path = "../../../util/panic-hook" }
parity-crypto = { version = "0.4.2", features = ["publickey"] }
parity-wordlist="1.2"
ethkey = { path = "../../crates/accounts/ethkey" }
panic_hook = { path = "../../crates/util/panic-hook" }
parity-crypto = { version = "0.6.2", features = [ "publickey" ] }
parity-wordlist="1.3"
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"

493
bin/ethkey/src/main.rs Normal file
View File

@ -0,0 +1,493 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
extern crate docopt;
extern crate env_logger;
extern crate ethkey;
extern crate panic_hook;
extern crate parity_crypto as crypto;
extern crate parity_wordlist;
extern crate rustc_hex;
extern crate serde;
extern crate threadpool;
#[macro_use]
extern crate serde_derive;
use std::{env, fmt, io, num::ParseIntError, process, sync};
use crypto::publickey::{
sign, verify_address, verify_public, Error as EthkeyError, Generator, KeyPair, Random,
};
use docopt::Docopt;
use ethkey::{brain_recover, Brain, BrainPrefix, Prefix};
use rustc_hex::{FromHex, FromHexError};
const USAGE: &'static str = r#"
Parity Ethereum keys generator.
Copyright 2015-2019 Parity Technologies (UK) Ltd.
Usage:
ethkey info <secret-or-phrase> [options]
ethkey generate random [options]
ethkey generate prefix <prefix> [options]
ethkey sign <secret> <message>
ethkey verify public <public> <signature> <message>
ethkey verify address <address> <signature> <message>
ethkey recover <address> <known-phrase>
ethkey [-h | --help]
Options:
-h, --help Display this message and exit.
-s, --secret Display only the secret key.
-p, --public Display only the public key.
-a, --address Display only the address.
-b, --brain Use parity brain wallet algorithm. Not recommended.
Commands:
info Display public key and address of the secret.
generate random Generates new random Ethereum key.
generate prefix Random generation, but address must start with a prefix ("vanity address").
sign Sign message using a secret key.
verify Verify signer of the signature by public key or address.
recover Try to find brain phrase matching given address from partial phrase.
"#;
#[derive(Debug, Deserialize)]
struct Args {
cmd_info: bool,
cmd_generate: bool,
cmd_random: bool,
cmd_prefix: bool,
cmd_sign: bool,
cmd_verify: bool,
cmd_public: bool,
cmd_address: bool,
cmd_recover: bool,
arg_prefix: String,
arg_secret: String,
arg_secret_or_phrase: String,
arg_known_phrase: String,
arg_message: String,
arg_public: String,
arg_address: String,
arg_signature: String,
flag_secret: bool,
flag_public: bool,
flag_address: bool,
flag_brain: bool,
}
#[derive(Debug)]
enum Error {
Ethkey(EthkeyError),
FromHex(FromHexError),
ParseInt(ParseIntError),
Docopt(docopt::Error),
Io(io::Error),
}
impl From<EthkeyError> for Error {
fn from(err: EthkeyError) -> Self {
Error::Ethkey(err)
}
}
impl From<FromHexError> for Error {
fn from(err: FromHexError) -> Self {
Error::FromHex(err)
}
}
impl From<ParseIntError> for Error {
fn from(err: ParseIntError) -> Self {
Error::ParseInt(err)
}
}
impl From<docopt::Error> for Error {
fn from(err: docopt::Error) -> Self {
Error::Docopt(err)
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Error::Ethkey(ref e) => write!(f, "{}", e),
Error::FromHex(ref e) => write!(f, "{}", e),
Error::ParseInt(ref e) => write!(f, "{}", e),
Error::Docopt(ref e) => write!(f, "{}", e),
Error::Io(ref e) => write!(f, "{}", e),
}
}
}
enum DisplayMode {
KeyPair,
Secret,
Public,
Address,
}
impl DisplayMode {
fn new(args: &Args) -> Self {
if args.flag_secret {
DisplayMode::Secret
} else if args.flag_public {
DisplayMode::Public
} else if args.flag_address {
DisplayMode::Address
} else {
DisplayMode::KeyPair
}
}
}
fn main() {
panic_hook::set_abort();
env_logger::try_init().expect("Logger initialized only once.");
match execute(env::args()) {
Ok(ok) => println!("{}", ok),
Err(Error::Docopt(ref e)) => e.exit(),
Err(err) => {
eprintln!("{}", err);
process::exit(1);
}
}
}
fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
let keypair = result.0;
match mode {
DisplayMode::KeyPair => match result.1 {
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
None => format!("{}", keypair),
},
DisplayMode::Secret => format!("{:x}", keypair.secret()),
DisplayMode::Public => format!("{:x}", keypair.public()),
DisplayMode::Address => format!("{:x}", keypair.address()),
}
}
fn execute<S, I>(command: I) -> Result<String, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?;
return if args.cmd_info {
let display_mode = DisplayMode::new(&args);
let result = if args.flag_brain {
let phrase = args.arg_secret_or_phrase;
let phrase_info = validate_phrase(&phrase);
let keypair = Brain::new(phrase).generate();
(keypair, Some(phrase_info))
} else {
let secret = args
.arg_secret_or_phrase
.parse()
.map_err(|_| EthkeyError::InvalidSecretKey)?;
(KeyPair::from_secret(secret)?, None)
};
Ok(display(result, display_mode))
} else if args.cmd_generate {
let display_mode = DisplayMode::new(&args);
let result = if args.cmd_random {
if args.flag_brain {
let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS);
let keypair = brain.generate()?;
let phrase = format!("recovery phrase: {}", brain.phrase());
(keypair, Some(phrase))
} else {
(Random.generate(), None)
}
} else if args.cmd_prefix {
let prefix = args.arg_prefix.from_hex()?;
let brain = args.flag_brain;
in_threads(move || {
let iterations = 1024;
let prefix = prefix.clone();
move || {
let prefix = prefix.clone();
let res = if brain {
let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS);
let result = brain.generate();
let phrase = format!("recovery phrase: {}", brain.phrase());
result.map(|keypair| (keypair, Some(phrase)))
} else {
let result = Prefix::new(prefix, iterations).generate();
result.map(|res| (res, None))
};
Ok(res.map(Some).unwrap_or(None))
}
})?
} else {
return Ok(format!("{}", USAGE));
};
Ok(display(result, display_mode))
} else if args.cmd_sign {
let secret = args
.arg_secret
.parse()
.map_err(|_| EthkeyError::InvalidSecretKey)?;
let message = args
.arg_message
.parse()
.map_err(|_| EthkeyError::InvalidMessage)?;
let signature = sign(&secret, &message)?;
Ok(format!("{}", signature))
} else if args.cmd_verify {
let signature = args
.arg_signature
.parse()
.map_err(|_| EthkeyError::InvalidSignature)?;
let message = args
.arg_message
.parse()
.map_err(|_| EthkeyError::InvalidMessage)?;
let ok = if args.cmd_public {
let public = args
.arg_public
.parse()
.map_err(|_| EthkeyError::InvalidPublicKey)?;
verify_public(&public, &signature, &message)?
} else if args.cmd_address {
let address = args
.arg_address
.parse()
.map_err(|_| EthkeyError::InvalidAddress)?;
verify_address(&address, &signature, &message)?
} else {
return Ok(format!("{}", USAGE));
};
Ok(format!("{}", ok))
} else if args.cmd_recover {
let display_mode = DisplayMode::new(&args);
let known_phrase = args.arg_known_phrase;
let address = args
.arg_address
.parse()
.map_err(|_| EthkeyError::InvalidAddress)?;
let (phrase, keypair) = in_threads(move || {
let mut it =
brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS);
move || {
let mut i = 0;
while let Some(phrase) = it.next() {
i += 1;
let keypair = Brain::new(phrase.clone()).generate();
if keypair.address() == address {
return Ok(Some((phrase, keypair)));
}
if i >= 1024 {
return Ok(None);
}
}
Err(EthkeyError::Custom("Couldn't find any results.".into()))
}
})?;
Ok(display((keypair, Some(phrase)), display_mode))
} else {
Ok(format!("{}", USAGE))
};
}
const BRAIN_WORDS: usize = 12;
fn validate_phrase(phrase: &str) -> String {
match Brain::validate_phrase(phrase, BRAIN_WORDS) {
Ok(()) => format!("The recovery phrase looks correct.\n"),
Err(err) => format!("The recover phrase was not generated by Parity: {}", err),
}
}
fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError>
where
O: Send + 'static,
X: Send + 'static,
F: Fn() -> X,
X: FnMut() -> Result<Option<O>, EthkeyError>,
{
let pool = threadpool::Builder::new().build();
let (tx, rx) = sync::mpsc::sync_channel(1);
let is_done = sync::Arc::new(sync::atomic::AtomicBool::default());
for _ in 0..pool.max_count() {
let is_done = is_done.clone();
let tx = tx.clone();
let mut task = prepare();
pool.execute(move || {
loop {
if is_done.load(sync::atomic::Ordering::SeqCst) {
return;
}
let res = match task() {
Ok(None) => continue,
Ok(Some(v)) => Ok(v),
Err(err) => Err(err),
};
// We are interested only in the first response.
let _ = tx.send(res);
}
});
}
if let Ok(solution) = rx.recv() {
is_done.store(true, sync::atomic::Ordering::SeqCst);
return solution;
}
Err(EthkeyError::Custom("No results found.".into()))
}
#[cfg(test)]
mod tests {
use super::execute;
#[test]
fn info() {
let command = vec![
"ethkey",
"info",
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected =
"secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn brain() {
let command = vec!["ethkey", "info", "--brain", "this is sparta"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected =
"The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn secret() {
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected =
"aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn public() {
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn address() {
let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn sign() {
let command = vec![
"ethkey",
"sign",
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
"bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987",
]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_valid_public() {
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "true".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_valid_address() {
let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "true".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
#[test]
fn verify_invalid() {
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected = "false".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
}

View File

@ -11,12 +11,10 @@ num_cpus = "1.6"
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"
parking_lot = "0.9"
ethstore = { path = "../" }
ethkey = { path = "../../ethkey" }
parity-crypto = { version = "0.4.2", features = ["publickey"] }
dir = { path = '../../../util/dir' }
panic_hook = { path = "../../../util/panic-hook" }
parking_lot = "0.11.1"
ethstore = { path = "../../crates/accounts/ethstore" }
dir = { path = '../../crates/util/dir' }
panic_hook = { path = "../../crates/util/panic-hook" }
[[bin]]
name = "ethstore"
@ -24,4 +22,4 @@ path = "src/main.rs"
doc = false
[dev-dependencies]
tempdir = "0.3"
tempdir = "0.3.5"

66
bin/ethstore/src/crack.rs Normal file
View File

@ -0,0 +1,66 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use parking_lot::Mutex;
use std::{cmp, collections::VecDeque, sync::Arc, thread};
use ethstore::{ethkey::Password, Error, PresaleWallet};
use num_cpus;
pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> {
let passwords = Arc::new(Mutex::new(passwords));
let mut handles = Vec::new();
for _ in 0..num_cpus::get() {
let passwords = passwords.clone();
let wallet = PresaleWallet::open(&wallet_path)?;
handles.push(thread::spawn(move || {
look_for_password(passwords, wallet);
}));
}
for handle in handles {
handle
.join()
.map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
}
Ok(())
}
fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleWallet) {
let mut counter = 0;
while !passwords.lock().is_empty() {
let package = {
let mut passwords = passwords.lock();
let len = passwords.len();
passwords.split_off(cmp::min(len, 32))
};
for pass in package {
counter += 1;
match wallet.decrypt(&pass) {
Ok(_) => {
println!("Found password: {}", pass.as_str());
passwords.lock().clear();
return;
}
_ if counter % 100 == 0 => print!("."),
_ => {}
}
}
}
}

363
bin/ethstore/src/main.rs Normal file
View File

@ -0,0 +1,363 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
extern crate dir;
extern crate docopt;
extern crate ethstore;
extern crate num_cpus;
extern crate panic_hook;
extern crate parking_lot;
extern crate rustc_hex;
extern crate serde;
extern crate env_logger;
#[macro_use]
extern crate serde_derive;
use std::{collections::VecDeque, env, fmt, fs, io::Read, process};
use docopt::Docopt;
use ethstore::{
accounts_dir::{KeyDirectory, RootDiskDirectory},
ethkey::{Address, Password},
import_accounts, EthStore, PresaleWallet, SecretStore, SecretVaultRef, SimpleSecretStore,
StoreAccountRef,
};
mod crack;
pub const USAGE: &'static str = r#"
Parity Ethereum key management tool.
Copyright 2015-2019 Parity Technologies (UK) Ltd.
Usage:
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore import [<password>] [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore find-wallet-pass <path> <password>
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore list-vaults [--dir DIR]
ethstore create-vault <vault> <password> [--dir DIR]
ethstore change-vault-pwd <vault> <old-pwd> <new-pwd> [--dir DIR]
ethstore move-to-vault <address> <vault> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore move-from-vault <address> <vault> <password> [--dir DIR]
ethstore [-h | --help]
Options:
-h, --help Display this message and exit.
--dir DIR Specify the secret store directory. It may be either
parity, parity-(chain), geth, geth-test
or a path [default: parity].
--vault VAULT Specify vault to use in this operation.
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
that this option is required when vault option is set.
Otherwise it is ignored.
--src DIR Specify import source. It may be either
parity, parity-(chain), geth, geth-test
or a path [default: geth].
Commands:
insert Save account with password.
change-pwd Change password.
list List accounts.
import Import accounts from src.
import-wallet Import presale wallet.
find-wallet-pass Tries to open a wallet with list of passwords given.
remove Remove account.
sign Sign message.
public Displays public key for an address.
list-vaults List vaults.
create-vault Create new vault.
change-vault-pwd Change vault password.
move-to-vault Move account to vault from another vault/root directory.
move-from-vault Move account to root directory from given vault.
"#;
#[derive(Debug, Deserialize)]
struct Args {
cmd_insert: bool,
cmd_change_pwd: bool,
cmd_list: bool,
cmd_import: bool,
cmd_import_wallet: bool,
cmd_find_wallet_pass: bool,
cmd_remove: bool,
cmd_sign: bool,
cmd_public: bool,
cmd_list_vaults: bool,
cmd_create_vault: bool,
cmd_change_vault_pwd: bool,
cmd_move_to_vault: bool,
cmd_move_from_vault: bool,
arg_secret: String,
arg_password: String,
arg_old_pwd: String,
arg_new_pwd: String,
arg_address: String,
arg_message: String,
arg_path: String,
arg_vault: String,
flag_src: String,
flag_dir: String,
flag_vault: String,
flag_vault_pwd: String,
}
enum Error {
Ethstore(ethstore::Error),
Docopt(docopt::Error),
}
impl From<ethstore::Error> for Error {
fn from(err: ethstore::Error) -> Self {
Error::Ethstore(err)
}
}
impl From<docopt::Error> for Error {
fn from(err: docopt::Error) -> Self {
Error::Docopt(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Ethstore(ref err) => fmt::Display::fmt(err, f),
Error::Docopt(ref err) => fmt::Display::fmt(err, f),
}
}
}
fn main() {
panic_hook::set_abort();
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "warn")
}
env_logger::try_init().expect("Logger initialized only once.");
match execute(env::args()) {
Ok(result) => println!("{}", result),
Err(Error::Docopt(ref e)) => e.exit(),
Err(err) => {
eprintln!("{}", err);
process::exit(1);
}
}
}
fn key_dir(location: &str, password: Option<Password>) -> Result<Box<dyn KeyDirectory>, Error> {
let dir: RootDiskDirectory = match location {
path if path.starts_with("parity") => {
let chain = path.split('-').nth(1).unwrap_or("ethereum");
let mut path = dir::default_data_pathbuf();
path.push("keys");
path.push(chain);
RootDiskDirectory::create(path)?
}
path => RootDiskDirectory::create(path)?,
};
Ok(Box::new(dir.with_password(password)))
}
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
if args.flag_vault.is_empty() {
return Ok(SecretVaultRef::Root);
}
let vault_pwd = load_password(&args.flag_vault_pwd)?;
store.open_vault(&args.flag_vault, &vault_pwd)?;
Ok(SecretVaultRef::Vault(args.flag_vault.clone()))
}
fn open_args_vault_account(
store: &EthStore,
address: Address,
args: &Args,
) -> Result<StoreAccountRef, Error> {
match open_args_vault(store, args)? {
SecretVaultRef::Root => Ok(StoreAccountRef::root(address)),
SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)),
}
}
fn format_accounts(accounts: &[Address]) -> String {
accounts
.iter()
.enumerate()
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
.collect::<Vec<String>>()
.join("\n")
}
fn format_vaults(vaults: &[String]) -> String {
vaults.join("\n")
}
fn load_password(path: &str) -> Result<Password, Error> {
let mut file = fs::File::open(path).map_err(|e| {
ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e))
})?;
let mut password = String::new();
file.read_to_string(&mut password).map_err(|e| {
ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e))
})?;
// drop EOF
let _ = password.pop();
Ok(password.into())
}
fn execute<S, I>(command: I) -> Result<String, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?;
let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
return if args.cmd_insert {
let secret = args
.arg_secret
.parse()
.map_err(|_| ethstore::Error::InvalidSecret)?;
let password = load_password(&args.arg_password)?;
let vault_ref = open_args_vault(&store, &args)?;
let account_ref = store.insert_account(vault_ref, secret, &password)?;
Ok(format!("0x{:x}", account_ref.address))
} else if args.cmd_change_pwd {
let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let old_pwd = load_password(&args.arg_old_pwd)?;
let new_pwd = load_password(&args.arg_new_pwd)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store
.change_password(&account_ref, &old_pwd, &new_pwd)
.is_ok();
Ok(format!("{}", ok))
} else if args.cmd_list {
let vault_ref = open_args_vault(&store, &args)?;
let accounts = store.accounts()?;
let accounts: Vec<_> = accounts
.into_iter()
.filter(|a| &a.vault == &vault_ref)
.map(|a| a.address)
.collect();
Ok(format_accounts(&accounts))
} else if args.cmd_import {
let password = match args.arg_password.as_ref() {
"" => None,
_ => Some(load_password(&args.arg_password)?),
};
let src = key_dir(&args.flag_src, password)?;
let dst = key_dir(&args.flag_dir, None)?;
let accounts = import_accounts(&*src, &*dst)?;
Ok(format_accounts(&accounts))
} else if args.cmd_import_wallet {
let wallet = PresaleWallet::open(&args.arg_path)?;
let password = load_password(&args.arg_password)?;
let kp = wallet.decrypt(&password)?;
let vault_ref = open_args_vault(&store, &args)?;
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
Ok(format!("0x{:x}", account_ref.address))
} else if args.cmd_find_wallet_pass {
let passwords = load_password(&args.arg_password)?;
let passwords = passwords
.as_str()
.lines()
.map(|line| str::to_owned(line).into())
.collect::<VecDeque<_>>();
crack::run(passwords, &args.arg_path)?;
Ok(format!("Password not found."))
} else if args.cmd_remove {
let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store.remove_account(&account_ref, &password).is_ok();
Ok(format!("{}", ok))
} else if args.cmd_sign {
let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let message = args
.arg_message
.parse()
.map_err(|_| ethstore::Error::InvalidMessage)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let signature = store.sign(&account_ref, &password, &message)?;
Ok(format!("0x{}", signature))
} else if args.cmd_public {
let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
let public = store.public(&account_ref, &password)?;
Ok(format!("0x{:x}", public))
} else if args.cmd_list_vaults {
let vaults = store.list_vaults()?;
Ok(format_vaults(&vaults))
} else if args.cmd_create_vault {
let password = load_password(&args.arg_password)?;
store.create_vault(&args.arg_vault, &password)?;
Ok("OK".to_owned())
} else if args.cmd_change_vault_pwd {
let old_pwd = load_password(&args.arg_old_pwd)?;
let new_pwd = load_password(&args.arg_new_pwd)?;
store.open_vault(&args.arg_vault, &old_pwd)?;
store.change_vault_password(&args.arg_vault, &new_pwd)?;
Ok("OK".to_owned())
} else if args.cmd_move_to_vault {
let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?;
store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?;
Ok("OK".to_owned())
} else if args.cmd_move_from_vault {
let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?;
store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(
SecretVaultRef::Root,
StoreAccountRef::vault(&args.arg_vault, address),
)?;
Ok("OK".to_owned())
} else {
Ok(format!("{}", USAGE))
};
}

33
bin/evmbin/Cargo.toml Normal file
View File

@ -0,0 +1,33 @@
[package]
description = "Parity EVM Implementation"
name = "evmbin"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[[bin]]
name = "openethereum-evm"
path = "./src/main.rs"
[dependencies]
common-types = { path = "../../crates/ethcore/types", features = ["test-helpers"] }
docopt = "1.0"
env_logger = "0.5"
ethcore = { path = "../../crates/ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] }
ethereum-types = "0.9.2"
ethjson = { path = "../../crates/ethjson" }
evm = { path = "../../crates/vm/evm" }
panic_hook = { path = "../../crates/util/panic-hook" }
parity-bytes = "0.1"
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
vm = { path = "../../crates/vm/vm" }
[dev-dependencies]
criterion = "0.3.0"
pretty_assertions = "0.1"
tempdir = "0.3"
[features]
evm-debug = ["ethcore/evm-debug-tests"]

60
bin/evmbin/README.md Normal file
View File

@ -0,0 +1,60 @@
## evmbin
EVM implementation for OpenEthereum.
### Usage
```
EVM implementation for Parity.
Copyright 2015-2020 Parity Technologies (UK) Ltd.
Usage:
openethereum-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only --omit-storage-output --omit-memory-output]
openethereum-evm stats [options]
openethereum-evm stats-jsontests-vm <file>
openethereum-evm [options]
openethereum-evm [-h | --help]
Commands:
state-test Run a state test from a json file.
stats Execute EVM runtime code and return the statistics.
stats-jsontests-vm Execute standard json-tests format VMTests and return
timing statistics in tsv format.
Transaction options:
--code CODE Contract code as hex (without 0x).
--to ADDRESS Recipient address (without 0x).
--from ADDRESS Sender address (without 0x).
--input DATA Input data as hex (without 0x).
--gas GAS Supplied gas as hex (without 0x).
--gas-price WEI Supplied gas price as hex (without 0x).
State test options:
--chain CHAIN Run only from specific chain name (i.e. one of EIP150, EIP158,
Frontier, Homestead, Byzantium, Constantinople,
ConstantinopleFix, Istanbul, EIP158ToByzantiumAt5, FrontierToHomesteadAt5,
HomesteadToDaoAt5, HomesteadToEIP150At5, Berlin, Yolo3).
--only NAME Runs only a single test matching the name.
General options:
--json Display verbose results in JSON.
--std-json Display results in standardized JSON format.
--std-err-only With --std-json redirect to err output only.
--std-out-only With --std-json redirect to out output only.
--omit-storage-output With --std-json omit storage output.
--omit-memory-output With --std-json omit memory output.
--std-dump-json Display results in standardized JSON format
with additional state dump.
Display result state dump in standardized JSON format.
--chain CHAIN Chain spec file path.
-h, --help Display this message and exit.
```
## OpenEthereum toolchain
_This project is a part of the OpenEthereum toolchain._
- [evmbin](https://github.com/openethereum/openethereum/blob/master/evmbin/) - EVM implementation for OpenEthereum
- [ethabi](https://github.com/openethereum/ethabi) - OpenEthereum function calls encoding.
- [ethstore](https://github.com/openethereum/openethereum/blob/master/accounts/ethstore) - OpenEthereum key management.
- [ethkey](https://github.com/openethereum/openethereum/blob/master/accounts/ethkey) - OpenEthereum keys generator.

View File

@ -1,18 +1,18 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// This file is part of OpenEthereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum is distributed in the hope that it will be useful,
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General 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/>.
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! benchmarking for EVM
//! should be started with:
@ -23,77 +23,76 @@
#[macro_use]
extern crate criterion;
extern crate ethcore;
extern crate evm;
extern crate ethereum_types;
extern crate evm;
extern crate rustc_hex;
extern crate vm;
use criterion::{black_box, Criterion};
use std::sync::Arc;
use criterion::{Criterion, black_box};
use ethereum_types::U256;
use evm::Factory;
use rustc_hex::FromHex;
use vm::tests::FakeExt;
use vm::{ActionParams, Ext};
use vm::{tests::FakeExt, ActionParams, Ext};
criterion_group!(
evmbin,
bench_simple_loop_usize,
bench_simple_loop_u256,
bench_rng_usize,
bench_rng_u256
evmbin,
bench_simple_loop_usize,
bench_simple_loop_u256,
bench_rng_usize,
bench_rng_u256
);
criterion_main!(evmbin);
fn bench_simple_loop_usize(c: &mut Criterion) {
simple_loop(U256::from(::std::usize::MAX), c, "simple_loop_usize")
simple_loop(U256::from(::std::usize::MAX), c, "simple_loop_usize")
}
fn bench_simple_loop_u256(c: &mut Criterion) {
simple_loop(!U256::zero(), c, "simple_loop_u256")
simple_loop(!U256::zero(), c, "simple_loop_u256")
}
fn simple_loop(gas: U256, c: &mut Criterion, bench_id: &str) {
let code = black_box(
let code = black_box(
"606060405260005b620042408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200".from_hex().unwrap()
);
c.bench_function(bench_id, move |b| {
b.iter(|| {
let mut params = ActionParams::default();
params.gas = gas;
params.code = Some(Arc::new(code.clone()));
c.bench_function(bench_id, move |b| {
b.iter(|| {
let mut params = ActionParams::default();
params.gas = gas;
params.code = Some(Arc::new(code.clone()));
let mut ext = FakeExt::new();
let evm = Factory::default().create(params, ext.schedule(), ext.depth());
let _ = evm.exec(&mut ext);
})
});
let mut ext = FakeExt::new();
let evm = Factory::default().create(params, ext.schedule(), ext.depth());
let _ = evm.exec(&mut ext);
})
});
}
fn bench_rng_usize(c: &mut Criterion) {
rng(U256::from(::std::usize::MAX), c, "rng_usize")
rng(U256::from(::std::usize::MAX), c, "rng_usize")
}
fn bench_rng_u256(c: &mut Criterion) {
rng(!U256::zero(), c, "rng_u256")
rng(!U256::zero(), c, "rng_u256")
}
fn rng(gas: U256, c: &mut Criterion, bench_id: &str) {
let code = black_box(
let code = black_box(
"6060604052600360056007600b60005b62004240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200".from_hex().unwrap()
);
c.bench_function(bench_id, move |b| {
b.iter(|| {
let mut params = ActionParams::default();
params.gas = gas;
params.code = Some(Arc::new(code.clone()));
c.bench_function(bench_id, move |b| {
b.iter(|| {
let mut params = ActionParams::default();
params.gas = gas;
params.code = Some(Arc::new(code.clone()));
let mut ext = FakeExt::new();
let evm = Factory::default().create(params, ext.schedule(), ext.depth());
let _ = evm.exec(&mut ext);
})
});
let mut ext = FakeExt::new();
let evm = Factory::default().create(params, ext.schedule(), ext.depth());
let _ = evm.exec(&mut ext);
})
});
}

View File

@ -0,0 +1,40 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Config used by display informants
#[derive(Default, Copy, Clone, Debug)]
pub struct Config {
omit_storage_output: bool,
omit_memory_output: bool,
}
impl Config {
pub fn new(omit_storage_output: bool, omit_memory_output: bool) -> Config {
Config {
omit_storage_output,
omit_memory_output,
}
}
pub fn omit_storage_output(&self) -> bool {
self.omit_storage_output
}
pub fn omit_memory_output(&self) -> bool {
self.omit_memory_output
}
}

View File

@ -0,0 +1,425 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! JSON VM output.
use std::{collections::HashMap, mem};
use super::config::Config;
use bytes::ToPretty;
use display;
use ethcore::trace;
use ethereum_types::{BigEndianHash, H256, U256};
use info as vm;
/// JSON formatting informant.
#[derive(Default)]
pub struct Informant {
code: Vec<u8>,
depth: usize,
pc: usize,
instruction: u8,
gas_cost: U256,
gas_used: U256,
mem_written: Option<(usize, usize)>,
store_written: Option<(U256, U256)>,
stack: Vec<U256>,
memory: Vec<u8>,
storage: HashMap<H256, H256>,
traces: Vec<String>,
subtraces: Vec<String>,
subinfos: Vec<Informant>,
subdepth: usize,
unmatched: bool,
config: Config,
}
impl Informant {
pub fn new(config: Config) -> Informant {
let mut def = Informant::default();
def.config = config;
def
}
fn with_informant_in_depth<F: Fn(&mut Informant)>(
informant: &mut Informant,
depth: usize,
f: F,
) {
if depth == 0 {
f(informant);
} else {
Self::with_informant_in_depth(
informant
.subinfos
.last_mut()
.expect("prepare/done_trace are not balanced"),
depth - 1,
f,
);
}
}
fn informant_trace(informant: &Informant, gas_used: U256) -> String {
let memory = if informant.config.omit_memory_output() {
"".to_string()
} else {
format!("0x{}", informant.memory.to_hex())
};
let storage = if informant.config.omit_storage_output() {
None
} else {
Some(&informant.storage)
};
let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info());
json!({
"pc": informant.pc,
"op": informant.instruction,
"opName": info.map(|i| i.name).unwrap_or(""),
"gas": format!("{:#x}", gas_used.saturating_add(informant.gas_cost)),
"gasCost": format!("{:#x}", informant.gas_cost),
"memory": memory,
"stack": informant.stack,
"storage": storage,
"depth": informant.depth,
})
.to_string()
}
}
impl vm::Informant for Informant {
type Sink = Config;
fn before_test(&mut self, name: &str, action: &str) {
println!("{}", json!({"action": action, "test": name}));
}
fn set_gas(&mut self, gas: U256) {
self.gas_used = gas;
}
fn clone_sink(&self) -> Self::Sink {
self.config
}
fn finish(result: vm::RunResult<Self::Output>, config: &mut Self::Sink) {
match result {
Ok(success) => {
for trace in success.traces.unwrap_or_else(Vec::new) {
println!("{}", trace);
}
let success_msg = json!({
"output": format!("0x{}", success.output.to_hex()),
"gasUsed": format!("{:#x}", success.gas_used),
"time": display::as_micros(&success.time),
});
println!("{}", success_msg)
}
Err(failure) => {
if !config.omit_storage_output() {
for trace in failure.traces.unwrap_or_else(Vec::new) {
println!("{}", trace);
}
}
let failure_msg = json!({
"error": &failure.error.to_string(),
"gasUsed": format!("{:#x}", failure.gas_used),
"time": display::as_micros(&failure.time),
});
println!("{}", failure_msg)
}
}
}
}
impl trace::VMTracer for Informant {
type Output = Vec<String>;
fn trace_next_instruction(&mut self, pc: usize, instruction: u8, _current_gas: U256) -> bool {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
informant.pc = pc;
informant.instruction = instruction;
informant.unmatched = true;
});
true
}
fn trace_prepare_execute(
&mut self,
pc: usize,
instruction: u8,
gas_cost: U256,
mem_written: Option<(usize, usize)>,
store_written: Option<(U256, U256)>,
) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
informant.pc = pc;
informant.instruction = instruction;
informant.gas_cost = gas_cost;
informant.mem_written = mem_written;
informant.store_written = store_written;
});
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
let store_diff = informant.store_written.clone();
let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info());
let trace = Self::informant_trace(informant, gas_used);
informant.traces.push(trace);
informant.unmatched = false;
informant.gas_used = gas_used;
let len = informant.stack.len();
let info_args = info.map(|i| i.args).unwrap_or(0);
informant
.stack
.truncate(if len > info_args { len - info_args } else { 0 });
informant.stack.extend_from_slice(stack_push);
// TODO [ToDr] Align memory?
if let Some((pos, size)) = informant.mem_written.clone() {
if informant.memory.len() < (pos + size) {
informant.memory.resize(pos + size, 0);
}
informant.memory[pos..(pos + size)].copy_from_slice(&mem[pos..(pos + size)]);
}
if let Some((pos, val)) = store_diff {
informant.storage.insert(
BigEndianHash::from_uint(&pos),
BigEndianHash::from_uint(&val),
);
}
if !informant.subtraces.is_empty() {
informant
.traces
.extend(mem::replace(&mut informant.subtraces, vec![]));
}
});
}
fn prepare_subtrace(&mut self, code: &[u8]) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
let mut vm = Informant::default();
vm.config = informant.config;
vm.depth = informant.depth + 1;
vm.code = code.to_vec();
vm.gas_used = informant.gas_used;
informant.subinfos.push(vm);
});
self.subdepth += 1;
}
fn done_subtrace(&mut self) {
self.subdepth -= 1;
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
if let Some(subtraces) = informant
.subinfos
.pop()
.expect("prepare/done_subtrace are not balanced")
.drain()
{
informant.subtraces.extend(subtraces);
}
});
}
fn drain(mut self) -> Option<Self::Output> {
if self.unmatched {
// print last line with final state:
self.gas_cost = 0.into();
let gas_used = self.gas_used;
let subdepth = self.subdepth;
Self::with_informant_in_depth(&mut self, subdepth, |informant: &mut Informant| {
let trace = Self::informant_trace(informant, gas_used);
informant.traces.push(trace);
});
} else if !self.subtraces.is_empty() {
self.traces
.extend(mem::replace(&mut self.subtraces, vec![]));
}
Some(self.traces)
}
}
#[cfg(test)]
mod tests {
use super::*;
use info::tests::run_test;
use serde_json;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
struct TestTrace {
pc: usize,
#[serde(rename = "op")]
instruction: u8,
op_name: String,
#[serde(rename = "gas")]
gas_used: U256,
gas_cost: U256,
memory: String,
stack: Vec<U256>,
storage: Option<HashMap<H256, H256>>,
depth: usize,
}
fn assert_traces_eq(a: &[String], b: &[String]) {
let mut ita = a.iter();
let mut itb = b.iter();
loop {
match (ita.next(), itb.next()) {
(Some(a), Some(b)) => {
// Compare both without worrying about the order of the fields
let actual: TestTrace = serde_json::from_str(a).unwrap();
let expected: TestTrace = serde_json::from_str(b).unwrap();
assert_eq!(actual, expected);
println!("{}", a);
}
(None, None) => return,
e => {
panic!("Traces mismatch: {:?}", e);
}
}
}
}
fn compare_json(traces: Option<Vec<String>>, expected: &str) {
let expected = expected
.split("\n")
.map(|x| x.trim())
.map(|x| x.to_owned())
.filter(|x| !x.is_empty())
.collect::<Vec<_>>();
assert_traces_eq(&traces.unwrap(), &expected);
}
#[test]
fn should_trace_failure() {
run_test(
Informant::default(),
&compare_json,
"60F8d6",
0xffff,
r#"
{"pc":0,"op":96,"opName":"PUSH1","gas":"0xffff","gasCost":"0x3","memory":"0x","stack":[],"storage":{},"depth":1}
{"pc":2,"op":214,"opName":"","gas":"0xfffc","gasCost":"0x0","memory":"0x","stack":["0xf8"],"storage":{},"depth":1}
"#,
);
run_test(
Informant::default(),
&compare_json,
"F8d6",
0xffff,
r#"
{"pc":0,"op":248,"opName":"","gas":"0xffff","gasCost":"0x0","memory":"0x","stack":[],"storage":{},"depth":1}
"#,
);
run_test(
Informant::default(),
&compare_json,
"5A51",
0xfffff,
r#"
{"depth":1,"gas":"0xfffff","gasCost":"0x2","memory":"0x","op":90,"opName":"GAS","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xffffd","gasCost":"0x0","memory":"0x","op":81,"opName":"MLOAD","pc":1,"stack":["0xffffd"],"storage":{}}
"#,
);
}
#[test]
fn should_trace_create_correctly() {
run_test(
Informant::default(),
&compare_json,
"32343434345830f138343438323439f0",
0xffff,
r#"
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":1}
{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0xfffd","gasCost":"0x2","memory":"0x","stack":["0x0"],"storage":{},"depth":1}
{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0xfffb","gasCost":"0x2","memory":"0x","stack":["0x0","0x0"],"storage":{},"depth":1}
{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0xfff9","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0"],"storage":{},"depth":1}
{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0xfff7","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":1}
{"pc":5,"op":88,"opName":"PC","gas":"0xfff5","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":1}
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0xfff3","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":1}
{"pc":7,"op":241,"opName":"CALL","gas":"0xfff1","gasCost":"0x61d0","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{},"depth":1}
{"pc":8,"op":56,"opName":"CODESIZE","gas":"0x9e21","gasCost":"0x2","memory":"0x","stack":["0x1"],"storage":{},"depth":1}
{"pc":9,"op":52,"opName":"CALLVALUE","gas":"0x9e1f","gasCost":"0x2","memory":"0x","stack":["0x1","0x10"],"storage":{},"depth":1}
{"pc":10,"op":52,"opName":"CALLVALUE","gas":"0x9e1d","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0"],"storage":{},"depth":1}
{"pc":11,"op":56,"opName":"CODESIZE","gas":"0x9e1b","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1}
{"pc":12,"op":50,"opName":"ORIGIN","gas":"0x9e19","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{},"depth":1}
{"pc":13,"op":52,"opName":"CALLVALUE","gas":"0x9e17","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{},"depth":1}
{"pc":14,"op":57,"opName":"CODECOPY","gas":"0x9e15","gasCost":"0x9","memory":"0x","stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{},"depth":1}
{"pc":15,"op":240,"opName":"CREATE","gas":"0x9e0c","gasCost":"0x9e0c","memory":"0x32343434345830f138343438323439f0","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1}
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0x210c","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":2}
{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0x210a","gasCost":"0x2","memory":"0x","stack":["0x0"],"storage":{},"depth":2}
{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0x2108","gasCost":"0x2","memory":"0x","stack":["0x0","0x0"],"storage":{},"depth":2}
{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0x2106","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0x2104","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":5,"op":88,"opName":"PC","gas":"0x2102","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0x2100","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":2}
{"pc":7,"op":241,"opName":"CALL","gas":"0x20fe","gasCost":"0x0","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{},"depth":2}
"#,
);
run_test(
Informant::default(),
&compare_json,
"3260D85554",
0xffff,
r#"
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":1}
{"pc":1,"op":96,"opName":"PUSH1","gas":"0xfffd","gasCost":"0x3","memory":"0x","stack":["0x0"],"storage":{},"depth":1}
{"pc":3,"op":85,"opName":"SSTORE","gas":"0xfffa","gasCost":"0x1388","memory":"0x","stack":["0x0","0xd8"],"storage":{},"depth":1}
{"pc":4,"op":84,"opName":"SLOAD","gas":"0xec72","gasCost":"0x0","memory":"0x","stack":[],"storage":{"0x00000000000000000000000000000000000000000000000000000000000000d8":"0x0000000000000000000000000000000000000000000000000000000000000000"},"depth":1}
"#,
);
}
#[test]
fn should_omit_storage_and_memory_flag() {
// should omit storage
run_test(
Informant::new(Config::new(true, true)),
&compare_json,
"3260D85554",
0xffff,
r#"
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"","stack":[],"storage":null,"depth":1}
{"pc":1,"op":96,"opName":"PUSH1","gas":"0xfffd","gasCost":"0x3","memory":"","stack":["0x0"],"storage":null,"depth":1}
{"pc":3,"op":85,"opName":"SSTORE","gas":"0xfffa","gasCost":"0x1388","memory":"","stack":["0x0","0xd8"],"storage":null,"depth":1}
{"pc":4,"op":84,"opName":"SLOAD","gas":"0xec72","gasCost":"0x0","memory":"","stack":[],"storage":null,"depth":1}
"#,
)
}
}

View File

@ -1,33 +1,34 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// This file is part of OpenEthereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum is distributed in the hope that it will be useful,
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General 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/>.
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! EVM output display utils.
//! VM Output display utils.
use std::time::Duration;
pub mod config;
pub mod json;
pub mod std_json;
pub mod simple;
pub mod std_json;
/// Formats duration into human readable format.
pub fn format_time(time: &Duration) -> String {
format!("{}.{:09}s", time.as_secs(), time.subsec_nanos())
format!("{}.{:.9}s", time.as_secs(), time.subsec_nanos())
}
/// Formats the time as microseconds.
pub fn as_micros(time: &Duration) -> u64 {
time.as_secs() * 1_000_000 + time.subsec_nanos() as u64 / 1_000
time.as_secs() * 1_000_000 + time.subsec_nanos() as u64 / 1_000
}

View File

@ -0,0 +1,74 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Simple VM output.
use super::config::Config;
use bytes::ToPretty;
use ethcore::trace;
use display;
use info as vm;
/// Simple formatting informant.
#[derive(Default)]
pub struct Informant {
config: Config,
}
impl Informant {
pub fn new(config: Config) -> Informant {
Informant { config }
}
}
impl vm::Informant for Informant {
type Sink = Config;
fn before_test(&mut self, name: &str, action: &str) {
println!("Test: {} ({})", name, action);
}
fn clone_sink(&self) -> Self::Sink {
self.config
}
fn finish(result: vm::RunResult<Self::Output>, _sink: &mut Self::Sink) {
match result {
Ok(success) => {
println!("Output: 0x{}", success.output.to_hex());
println!("Gas used: {:x}", success.gas_used);
println!("Time: {}", display::format_time(&success.time));
}
Err(failure) => {
println!("Error: {}", failure.error);
println!("Time: {}", display::format_time(&failure.time));
}
}
}
}
impl trace::VMTracer for Informant {
type Output = ();
fn prepare_subtrace(&mut self, _code: &[u8]) {
Default::default()
}
fn done_subtrace(&mut self) {}
fn drain(self) -> Option<()> {
None
}
}

View File

@ -0,0 +1,413 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Standardized JSON VM output.
use std::{collections::HashMap, io};
use super::config::Config;
use bytes::ToPretty;
use display;
use ethcore::{pod_state, trace};
use ethereum_types::{BigEndianHash, H256, U256};
use info as vm;
pub trait Writer: io::Write + Send + Sized {
fn clone(&self) -> Self;
fn default() -> Self;
}
impl Writer for io::Stdout {
fn clone(&self) -> Self {
io::stdout()
}
fn default() -> Self {
io::stdout()
}
}
impl Writer for io::Stderr {
fn clone(&self) -> Self {
io::stderr()
}
fn default() -> Self {
io::stderr()
}
}
/// JSON formatting informant.
pub struct Informant<Trace, Out> {
code: Vec<u8>,
instruction: u8,
depth: usize,
stack: Vec<U256>,
storage: HashMap<H256, H256>,
subinfos: Vec<Informant<Trace, Out>>,
subdepth: usize,
trace_sink: Trace,
out_sink: Out,
config: Config,
}
impl Default for Informant<io::Stderr, io::Stdout> {
fn default() -> Self {
Self::new(io::stderr(), io::stdout(), Config::default())
}
}
impl Informant<io::Stdout, io::Stdout> {
/// std json informant using out only.
pub fn out_only(config: Config) -> Self {
Self::new(io::stdout(), io::stdout(), config)
}
}
impl Informant<io::Stderr, io::Stderr> {
/// std json informant using err only.
pub fn err_only(config: Config) -> Self {
Self::new(io::stderr(), io::stderr(), config)
}
}
impl Informant<io::Stderr, io::Stdout> {
pub fn new_default(config: Config) -> Self {
let mut informant = Self::default();
informant.config = config;
informant
}
}
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
pub fn new(trace_sink: Trace, out_sink: Out, config: Config) -> Self {
Informant {
code: Default::default(),
instruction: Default::default(),
depth: Default::default(),
stack: Default::default(),
storage: Default::default(),
subinfos: Default::default(),
subdepth: 0,
trace_sink,
out_sink,
config,
}
}
fn with_informant_in_depth<F: Fn(&mut Informant<Trace, Out>)>(
informant: &mut Informant<Trace, Out>,
depth: usize,
f: F,
) {
if depth == 0 {
f(informant);
} else {
Self::with_informant_in_depth(
informant
.subinfos
.last_mut()
.expect("prepare/done_trace are not balanced"),
depth - 1,
f,
);
}
}
fn dump_state_into(
trace_sink: &mut Trace,
root: H256,
end_state: &Option<pod_state::PodState>,
) {
if let Some(ref end_state) = end_state {
let dump_data = json!({
"root": root,
"accounts": end_state,
});
writeln!(trace_sink, "{}", dump_data).expect("The sink must be writeable.");
}
}
}
impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
type Sink = (Trace, Out, Config);
fn before_test(&mut self, name: &str, action: &str) {
let out_data = json!({
"action": action,
"test": name,
});
writeln!(&mut self.out_sink, "{}", out_data).expect("The sink must be writeable.");
}
fn set_gas(&mut self, _gas: U256) {}
fn clone_sink(&self) -> Self::Sink {
(
self.trace_sink.clone(),
self.out_sink.clone(),
self.config.clone(),
)
}
fn finish(
result: vm::RunResult<<Self as trace::VMTracer>::Output>,
(ref mut trace_sink, ref mut out_sink, _): &mut Self::Sink,
) {
match result {
Ok(success) => {
let trace_data = json!({"stateRoot": success.state_root});
writeln!(trace_sink, "{}", trace_data).expect("The sink must be writeable.");
Self::dump_state_into(trace_sink, success.state_root, &success.end_state);
let out_data = json!({
"output": format!("0x{}", success.output.to_hex()),
"gasUsed": format!("{:#x}", success.gas_used),
"time": display::as_micros(&success.time),
});
writeln!(out_sink, "{}", out_data).expect("The sink must be writeable.");
}
Err(failure) => {
let out_data = json!({
"error": &failure.error.to_string(),
"gasUsed": format!("{:#x}", failure.gas_used),
"time": display::as_micros(&failure.time),
});
Self::dump_state_into(trace_sink, failure.state_root, &failure.end_state);
writeln!(out_sink, "{}", out_data).expect("The sink must be writeable.");
}
}
}
}
impl<Trace: Writer, Out: Writer> trace::VMTracer for Informant<Trace, Out> {
type Output = ();
fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
let storage = if informant.config.omit_storage_output() {
None
} else {
Some(&informant.storage)
};
let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info());
informant.instruction = instruction;
let trace_data = json!({
"pc": pc,
"op": instruction,
"opName": info.map(|i| i.name).unwrap_or(""),
"gas": format!("{:#x}", current_gas),
"stack": informant.stack,
"storage": storage,
"depth": informant.depth,
});
writeln!(&mut informant.trace_sink, "{}", trace_data)
.expect("The sink must be writeable.");
});
true
}
fn trace_prepare_execute(
&mut self,
_pc: usize,
_instruction: u8,
_gas_cost: U256,
_mem_written: Option<(usize, usize)>,
store_written: Option<(U256, U256)>,
) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
if let Some((pos, val)) = store_written {
informant.storage.insert(
BigEndianHash::from_uint(&pos),
BigEndianHash::from_uint(&val),
);
}
});
}
fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem: &[u8]) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info());
let len = informant.stack.len();
let info_args = info.map(|i| i.args).unwrap_or(0);
informant
.stack
.truncate(if len > info_args { len - info_args } else { 0 });
informant.stack.extend_from_slice(stack_push);
});
}
fn prepare_subtrace(&mut self, code: &[u8]) {
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
let mut vm = Informant::new(
informant.trace_sink.clone(),
informant.out_sink.clone(),
informant.config,
);
vm.depth = informant.depth + 1;
vm.code = code.to_vec();
informant.subinfos.push(vm);
});
self.subdepth += 1;
}
fn done_subtrace(&mut self) {
self.subdepth -= 1;
let subdepth = self.subdepth;
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
informant.subinfos.pop();
});
}
fn drain(self) -> Option<Self::Output> {
None
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use info::tests::run_test;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Default)]
pub struct TestWriter(pub Arc<Mutex<Vec<u8>>>);
impl Writer for TestWriter {
fn clone(&self) -> Self {
Clone::clone(self)
}
fn default() -> Self {
Default::default()
}
}
impl io::Write for TestWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.lock().unwrap().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.lock().unwrap().flush()
}
}
pub fn informant(config: Config) -> (Informant<TestWriter, TestWriter>, Arc<Mutex<Vec<u8>>>) {
let trace_writer: TestWriter = Default::default();
let out_writer: TestWriter = Default::default();
let res = trace_writer.0.clone();
(Informant::new(trace_writer, out_writer, config), res)
}
#[test]
fn should_trace_failure() {
let (inf, res) = informant(Config::default());
run_test(
inf,
move |_, expected| {
let bytes = res.lock().unwrap();
assert_eq!(expected, &String::from_utf8_lossy(&**bytes))
},
"60F8d6",
0xffff,
r#"{"depth":1,"gas":"0xffff","op":96,"opName":"PUSH1","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xfffc","op":214,"opName":"","pc":2,"stack":["0xf8"],"storage":{}}
"#,
);
let (inf, res) = informant(Config::default());
run_test(
inf,
move |_, expected| {
let bytes = res.lock().unwrap();
assert_eq!(expected, &String::from_utf8_lossy(&**bytes))
},
"F8d6",
0xffff,
r#"{"depth":1,"gas":"0xffff","op":248,"opName":"","pc":0,"stack":[],"storage":{}}
"#,
);
}
#[test]
fn should_trace_create_correctly() {
let (informant, res) = informant(Config::default());
run_test(
informant,
move |_, expected| {
let bytes = res.lock().unwrap();
assert_eq!(expected, &String::from_utf8_lossy(&**bytes))
},
"32343434345830f138343438323439f0",
0xffff,
r#"{"depth":1,"gas":"0xffff","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xfffd","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}}
{"depth":1,"gas":"0xfffb","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff9","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff7","op":52,"opName":"CALLVALUE","pc":4,"stack":["0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff5","op":88,"opName":"PC","pc":5,"stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff3","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}}
{"depth":1,"gas":"0xfff1","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e21","op":56,"opName":"CODESIZE","pc":8,"stack":["0x1"],"storage":{}}
{"depth":1,"gas":"0x9e1f","op":52,"opName":"CALLVALUE","pc":9,"stack":["0x1","0x10"],"storage":{}}
{"depth":1,"gas":"0x9e1d","op":52,"opName":"CALLVALUE","pc":10,"stack":["0x1","0x10","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e1b","op":56,"opName":"CODESIZE","pc":11,"stack":["0x1","0x10","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e19","op":50,"opName":"ORIGIN","pc":12,"stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{}}
{"depth":1,"gas":"0x9e17","op":52,"opName":"CALLVALUE","pc":13,"stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e15","op":57,"opName":"CODECOPY","pc":14,"stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e0c","op":240,"opName":"CREATE","pc":15,"stack":["0x1","0x10","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x210c","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}}
{"depth":2,"gas":"0x210a","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}}
{"depth":2,"gas":"0x2108","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2106","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2104","op":52,"opName":"CALLVALUE","pc":4,"stack":["0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2102","op":88,"opName":"PC","pc":5,"stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2100","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}}
{"depth":2,"gas":"0x20fe","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{}}
"#,
)
}
#[test]
fn should_omit_storage_and_memory_flag() {
// should omit storage
let (informant, res) = informant(Config::new(true, true));
run_test(
informant,
move |_, expected| {
let bytes = res.lock().unwrap();
assert_eq!(expected, &String::from_utf8_lossy(&**bytes))
},
"3260D85554",
0xffff,
r#"{"depth":1,"gas":"0xffff","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":null}
{"depth":1,"gas":"0xfffd","op":96,"opName":"PUSH1","pc":1,"stack":["0x0"],"storage":null}
{"depth":1,"gas":"0xfffa","op":85,"opName":"SSTORE","pc":3,"stack":["0x0","0xd8"],"storage":null}
{"depth":1,"gas":"0xec72","op":84,"opName":"SLOAD","pc":4,"stack":[],"storage":null}
"#,
)
}
}

316
bin/evmbin/src/info.rs Normal file
View File

@ -0,0 +1,316 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! VM runner.
use ethcore::{
client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess},
pod_state, spec, state, state_db, trace, TrieSpec,
};
use ethereum_types::{H256, U256};
use ethjson;
use std::time::{Duration, Instant};
use types::transaction;
use vm::ActionParams;
/// VM execution informant
pub trait Informant: trace::VMTracer {
/// Sink to use with finish
type Sink;
/// Display a single run init message
fn before_test(&mut self, test: &str, action: &str);
/// Set initial gas.
fn set_gas(&mut self, _gas: U256) {}
/// Clone sink.
fn clone_sink(&self) -> Self::Sink;
/// Display final result.
fn finish(result: RunResult<Self::Output>, &mut Self::Sink);
}
/// Execution finished correctly
#[derive(Debug)]
pub struct Success<T> {
/// State root
pub state_root: H256,
/// Used gas
pub gas_used: U256,
/// Output as bytes
pub output: Vec<u8>,
/// Time Taken
pub time: Duration,
/// Traces
pub traces: Option<T>,
/// Optional end state dump
pub end_state: Option<pod_state::PodState>,
}
/// Execution failed
#[derive(Debug)]
pub struct Failure<T> {
/// State root
pub state_root: H256,
/// Used gas
pub gas_used: U256,
/// Internal error
pub error: EvmTestError,
/// Duration
pub time: Duration,
/// Traces
pub traces: Option<T>,
/// Optional end state dump
pub end_state: Option<pod_state::PodState>,
}
/// EVM Execution result
pub type RunResult<T> = Result<Success<T>, Failure<T>>;
/// Execute given `ActionParams` and return the result.
pub fn run_action<T: Informant>(
spec: &spec::Spec,
mut params: ActionParams,
mut informant: T,
trie_spec: TrieSpec,
) -> RunResult<T::Output> {
informant.set_gas(params.gas);
// if the code is not overwritten from CLI, use code from spec file.
if params.code.is_none() {
if let Some(acc) = spec.genesis_state().get().get(&params.code_address) {
params.code = acc.code.clone().map(::std::sync::Arc::new);
params.code_hash = None;
}
}
run(
spec,
trie_spec,
params.gas,
spec.genesis_state(),
|mut client| {
let result = match client.call(params, &mut trace::NoopTracer, &mut informant) {
Ok(r) => (Ok(r.return_data.to_vec()), Some(r.gas_left)),
Err(err) => (Err(err), None),
};
(result.0, H256::zero(), None, result.1, informant.drain())
},
)
}
/// Execute given Transaction and verify resulting state root.
pub fn run_transaction<T: Informant>(
name: &str,
idx: usize,
spec: &ethjson::spec::ForkSpec,
pre_state: &pod_state::PodState,
post_root: H256,
env_info: &client::EnvInfo,
transaction: transaction::SignedTransaction,
mut informant: T,
trie_spec: TrieSpec,
) {
let spec_name = format!("{:?}", spec).to_lowercase();
let spec = match EvmTestClient::spec_from_json(spec) {
Some(spec) => {
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting");
spec
}
None => {
informant.before_test(
&format!("{}:{}:{}", name, spec_name, idx),
"skipping because of missing spec",
);
return;
}
};
informant.set_gas(env_info.gas_limit);
let mut sink = informant.clone_sink();
let result = run(
&spec,
trie_spec,
transaction.tx().gas,
pre_state,
|mut client| {
let result = client.transact(env_info, transaction, trace::NoopTracer, informant);
match result {
Ok(TransactSuccess {
state_root,
gas_left,
output,
vm_trace,
end_state,
..
}) => {
if state_root != post_root {
(
Err(EvmTestError::PostCondition(format!(
"State root mismatch (got: {:#x}, expected: {:#x})",
state_root, post_root,
))),
state_root,
end_state,
Some(gas_left),
None,
)
} else {
(Ok(output), state_root, end_state, Some(gas_left), vm_trace)
}
}
Err(TransactErr {
state_root,
error,
end_state,
}) => (
Err(EvmTestError::PostCondition(format!(
"Unexpected execution error: {:?}",
error
))),
state_root,
end_state,
None,
None,
),
}
},
);
T::finish(result, &mut sink)
}
fn dump_state(state: &state::State<state_db::StateDB>) -> Option<pod_state::PodState> {
state.to_pod_full().ok()
}
/// Execute VM with given `ActionParams`
pub fn run<'a, F, X>(
spec: &'a spec::Spec,
trie_spec: TrieSpec,
initial_gas: U256,
pre_state: &'a pod_state::PodState,
run: F,
) -> RunResult<X>
where
F: FnOnce(
EvmTestClient,
) -> (
Result<Vec<u8>, EvmTestError>,
H256,
Option<pod_state::PodState>,
Option<U256>,
Option<X>,
),
{
let do_dump = trie_spec == TrieSpec::Fat;
let mut test_client =
EvmTestClient::from_pod_state_with_trie(spec, pre_state.clone(), trie_spec).map_err(
|error| Failure {
gas_used: 0.into(),
error,
time: Duration::from_secs(0),
traces: None,
state_root: H256::default(),
end_state: None,
},
)?;
if do_dump {
test_client.set_dump_state_fn(dump_state);
}
let start = Instant::now();
let result = run(test_client);
let time = start.elapsed();
match result {
(Ok(output), state_root, end_state, gas_left, traces) => Ok(Success {
state_root,
gas_used: gas_left
.map(|gas_left| initial_gas - gas_left)
.unwrap_or(initial_gas),
output,
time,
traces,
end_state,
}),
(Err(error), state_root, end_state, gas_left, traces) => Err(Failure {
gas_used: gas_left
.map(|gas_left| initial_gas - gas_left)
.unwrap_or(initial_gas),
error,
time,
traces,
state_root,
end_state,
}),
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use ethereum_types::Address;
use rustc_hex::FromHex;
use std::sync::Arc;
use tempdir::TempDir;
pub fn run_test<T, I, F>(informant: I, compare: F, code: &str, gas: T, expected: &str)
where
T: Into<U256>,
I: Informant,
F: FnOnce(Option<I::Output>, &str),
{
let mut params = ActionParams::default();
params.code = Some(Arc::new(code.from_hex().unwrap()));
params.gas = gas.into();
let tempdir = TempDir::new("").unwrap();
let spec = ::ethcore::ethereum::new_foundation(&tempdir.path());
let result = run_action(&spec, params, informant, TrieSpec::Secure);
match result {
Ok(Success { traces, .. }) => compare(traces, expected),
Err(Failure { traces, .. }) => compare(traces, expected),
}
}
#[test]
fn should_call_account_from_spec() {
use display::{config::Config, std_json::tests::informant};
let (inf, res) = informant(Config::default());
let mut params = ActionParams::default();
params.code_address = Address::from_low_u64_be(0x20);
params.gas = 0xffff.into();
let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json"));
let _result = run_action(&spec, params, inf, TrieSpec::Secure);
assert_eq!(
&String::from_utf8_lossy(&**res.lock().unwrap()),
r#"{"depth":1,"gas":"0xffff","op":98,"opName":"PUSH3","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xfffc","op":96,"opName":"PUSH1","pc":4,"stack":["0xaaaaaa"],"storage":{}}
{"depth":1,"gas":"0xfff9","op":96,"opName":"PUSH1","pc":6,"stack":["0xaaaaaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xfff6","op":80,"opName":"POP","pc":8,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xfff4","op":96,"opName":"PUSH1","pc":9,"stack":["0xaaaaaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xfff1","op":96,"opName":"PUSH1","pc":11,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffee","op":96,"opName":"PUSH1","pc":13,"stack":["0xaaaaaa","0xaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffeb","op":96,"opName":"PUSH1","pc":15,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffe8","op":96,"opName":"PUSH1","pc":17,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffe5","op":96,"opName":"PUSH1","pc":19,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}}
"#
);
}
}

511
bin/evmbin/src/main.rs Normal file
View File

@ -0,0 +1,511 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! OpenEthereum EVM interpreter binary.
#![warn(missing_docs)]
extern crate common_types as types;
extern crate ethcore;
extern crate ethjson;
extern crate rustc_hex;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate docopt;
extern crate env_logger;
extern crate ethereum_types;
extern crate evm;
extern crate panic_hook;
extern crate parity_bytes as bytes;
extern crate vm;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
#[cfg(test)]
extern crate tempdir;
use bytes::Bytes;
use docopt::Docopt;
use ethcore::{json_tests, spec, TrieSpec};
use ethereum_types::{Address, U256};
use ethjson::spec::ForkSpec;
use evm::EnvInfo;
use rustc_hex::FromHex;
use std::{fmt, fs, path::PathBuf, sync::Arc};
use vm::{ActionParams, CallType};
mod display;
mod info;
use info::Informant;
const USAGE: &'static str = r#"
EVM implementation for Parity.
Copyright 2015-2020 Parity Technologies (UK) Ltd.
Usage:
openethereum-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only --omit-storage-output --omit-memory-output]
openethereum-evm stats [options]
openethereum-evm stats-jsontests-vm <file>
openethereum-evm [options]
openethereum-evm [-h | --help]
Commands:
state-test Run a state test from a json file.
stats Execute EVM runtime code and return the statistics.
stats-jsontests-vm Execute standard json-tests format VMTests and return
timing statistics in tsv format.
Transaction options:
--code CODE Contract code as hex (without 0x).
--to ADDRESS Recipient address (without 0x).
--from ADDRESS Sender address (without 0x).
--input DATA Input data as hex (without 0x).
--gas GAS Supplied gas as hex (without 0x).
--gas-price WEI Supplied gas price as hex (without 0x).
State test options:
--chain CHAIN Run only from specific chain name (i.e. one of EIP150, EIP158,
Frontier, Homestead, Byzantium, Constantinople,
ConstantinopleFix, Istanbul, EIP158ToByzantiumAt5, FrontierToHomesteadAt5,
HomesteadToDaoAt5, HomesteadToEIP150At5, Berlin, Yolo3).
--only NAME Runs only a single test matching the name.
General options:
--json Display verbose results in JSON.
--std-json Display results in standardized JSON format.
--std-err-only With --std-json redirect to err output only.
--std-out-only With --std-json redirect to out output only.
--omit-storage-output With --std-json omit storage output.
--omit-memory-output With --std-json omit memory output.
--std-dump-json Display results in standardized JSON format
with additional state dump.
Display result state dump in standardized JSON format.
--chain CHAIN Chain spec file path.
-h, --help Display this message and exit.
"#;
fn main() {
panic_hook::set_abort();
env_logger::init();
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.deserialize())
.unwrap_or_else(|e| e.exit());
let config = args.config();
if args.cmd_state_test {
run_state_test(args)
} else if args.cmd_stats_jsontests_vm {
run_stats_jsontests_vm(args)
} else if args.flag_json {
run_call(args, display::json::Informant::new(config))
} else if args.flag_std_dump_json || args.flag_std_json {
if args.flag_std_err_only {
run_call(args, display::std_json::Informant::err_only(config))
} else if args.flag_std_out_only {
run_call(args, display::std_json::Informant::out_only(config))
} else {
run_call(args, display::std_json::Informant::new_default(config))
};
} else {
run_call(args, display::simple::Informant::new(config))
}
}
fn run_stats_jsontests_vm(args: Args) {
use json_tests::HookType;
use std::{
collections::HashMap,
time::{Duration, Instant},
};
let file = args.arg_file.expect("FILE (or PATH) is required");
let mut timings: HashMap<String, (Instant, Option<Duration>)> = HashMap::new();
{
let mut record_time = |name: &str, typ: HookType| match typ {
HookType::OnStart => {
timings.insert(name.to_string(), (Instant::now(), None));
}
HookType::OnStop => {
timings.entry(name.to_string()).and_modify(|v| {
v.1 = Some(v.0.elapsed());
});
}
};
for file_path in json_tests::find_json_files_recursive(&file) {
let json_data = std::fs::read(&file_path).unwrap();
json_tests::json_executive_test(&file_path, &json_data, &mut record_time);
}
}
for (name, v) in timings {
println!(
"{}\t{}",
name,
display::as_micros(&v.1.expect("All hooks are called with OnStop; qed"))
);
}
}
fn run_state_test(args: Args) {
use ethjson::state::test::Test;
let config = args.config();
let file = args.arg_file.expect("FILE is required");
let mut file = match fs::File::open(&file) {
Err(err) => die(format!("Unable to open: {:?}: {}", file, err)),
Ok(file) => file,
};
let state_test = match Test::load(&mut file) {
Err(err) => die(format!("Unable to load the test file: {}", err)),
Ok(test) => test,
};
let only_test = args.flag_only.map(|s| s.to_lowercase());
let only_chain = args.flag_chain.map(|s| s.to_lowercase());
for (name, test) in state_test {
if let Some(false) = only_test
.as_ref()
.map(|only_test| &name.to_lowercase() == only_test)
{
continue;
}
let multitransaction = test.transaction;
let env_info: EnvInfo = test.env.into();
let pre = test.pre_state.into();
for (spec, states) in test.post_states {
//hardcode base fee for part of the london tests, that miss base fee field in env
let mut test_env = env_info.clone();
if spec >= ForkSpec::London {
if test_env.base_fee.is_none() {
test_env.base_fee = Some(0x0a.into());
}
}
if let Some(false) = only_chain
.as_ref()
.map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain)
{
continue;
}
for (idx, state) in states.into_iter().enumerate() {
let post_root = state.hash.into();
let transaction = multitransaction.select(&state.indexes);
let trie_spec = if args.flag_std_dump_json {
TrieSpec::Fat
} else {
TrieSpec::Secure
};
if args.flag_json {
info::run_transaction(
&name,
idx,
&spec,
&pre,
post_root,
&test_env,
transaction,
display::json::Informant::new(config),
trie_spec,
)
} else if args.flag_std_dump_json || args.flag_std_json {
if args.flag_std_err_only {
info::run_transaction(
&name,
idx,
&spec,
&pre,
post_root,
&test_env,
transaction,
display::std_json::Informant::err_only(config),
trie_spec,
)
} else if args.flag_std_out_only {
info::run_transaction(
&name,
idx,
&spec,
&pre,
post_root,
&test_env,
transaction,
display::std_json::Informant::out_only(config),
trie_spec,
)
} else {
info::run_transaction(
&name,
idx,
&spec,
&pre,
post_root,
&test_env,
transaction,
display::std_json::Informant::new_default(config),
trie_spec,
)
}
} else {
info::run_transaction(
&name,
idx,
&spec,
&pre,
post_root,
&test_env,
transaction,
display::simple::Informant::new(config),
trie_spec,
)
}
}
}
}
}
fn run_call<T: Informant>(args: Args, informant: T) {
let from = arg(args.from(), "--from");
let to = arg(args.to(), "--to");
let code = arg(args.code(), "--code");
let spec = arg(args.spec(), "--chain");
let gas = arg(args.gas(), "--gas");
let gas_price = arg(args.gas_price(), "--gas-price");
let data = arg(args.data(), "--input");
if code.is_none() && to == Address::default() {
die("Either --code or --to is required.");
}
let mut params = ActionParams::default();
if spec.engine.params().eip2929_transition == 0 {
params.access_list.enable();
params.access_list.insert_address(from);
params.access_list.insert_address(to);
for (builtin, _) in spec.engine.builtins() {
params.access_list.insert_address(*builtin);
}
}
params.call_type = if code.is_none() {
CallType::Call
} else {
CallType::None
};
params.code_address = to;
params.address = to;
params.sender = from;
params.origin = from;
params.gas = gas;
params.gas_price = gas_price;
params.code = code.map(Arc::new);
params.data = data;
let mut sink = informant.clone_sink();
let result = if args.flag_std_dump_json {
info::run_action(&spec, params, informant, TrieSpec::Fat)
} else {
info::run_action(&spec, params, informant, TrieSpec::Secure)
};
T::finish(result, &mut sink);
}
#[derive(Debug, Deserialize)]
struct Args {
cmd_stats: bool,
cmd_state_test: bool,
cmd_stats_jsontests_vm: bool,
arg_file: Option<PathBuf>,
flag_only: Option<String>,
flag_from: Option<String>,
flag_to: Option<String>,
flag_code: Option<String>,
flag_gas: Option<String>,
flag_gas_price: Option<String>,
flag_input: Option<String>,
flag_chain: Option<String>,
flag_json: bool,
flag_std_json: bool,
flag_std_dump_json: bool,
flag_std_err_only: bool,
flag_std_out_only: bool,
flag_omit_storage_output: bool,
flag_omit_memory_output: bool,
}
impl Args {
pub fn gas(&self) -> Result<U256, String> {
match self.flag_gas {
Some(ref gas) => gas.parse().map_err(to_string),
None => Ok(U256::from(u64::max_value())),
}
}
pub fn gas_price(&self) -> Result<U256, String> {
match self.flag_gas_price {
Some(ref gas_price) => gas_price.parse().map_err(to_string),
None => Ok(U256::zero()),
}
}
pub fn from(&self) -> Result<Address, String> {
match self.flag_from {
Some(ref from) => from.parse().map_err(to_string),
None => Ok(Address::default()),
}
}
pub fn to(&self) -> Result<Address, String> {
match self.flag_to {
Some(ref to) => to.parse().map_err(to_string),
None => Ok(Address::default()),
}
}
pub fn code(&self) -> Result<Option<Bytes>, String> {
match self.flag_code {
Some(ref code) => code.from_hex().map(Some).map_err(to_string),
None => Ok(None),
}
}
pub fn data(&self) -> Result<Option<Bytes>, String> {
match self.flag_input {
Some(ref input) => input.from_hex().map_err(to_string).map(Some),
None => Ok(None),
}
}
pub fn spec(&self) -> Result<spec::Spec, String> {
Ok(match self.flag_chain {
Some(ref spec_name) => {
let fork_spec: Result<ethjson::spec::ForkSpec, _> =
serde_json::from_str(&format!("{:?}", spec_name));
if let Ok(fork_spec) = fork_spec {
ethcore::client::EvmTestClient::spec_from_json(&fork_spec)
.expect("this forkspec is not defined")
} else {
let file = fs::File::open(spec_name).map_err(|e| format!("{}", e))?;
spec::Spec::load(&::std::env::temp_dir(), file)?
}
}
None => ethcore::ethereum::new_foundation(&::std::env::temp_dir()),
})
}
pub fn config(&self) -> display::config::Config {
display::config::Config::new(self.flag_omit_storage_output, self.flag_omit_memory_output)
}
}
fn arg<T>(v: Result<T, String>, param: &str) -> T {
v.unwrap_or_else(|e| die(format!("Invalid {}: {}", param, e)))
}
fn to_string<T: fmt::Display>(msg: T) -> String {
format!("{}", msg)
}
fn die<T: fmt::Display>(msg: T) -> ! {
println!("{}", msg);
::std::process::exit(-1)
}
#[cfg(test)]
mod tests {
use super::{Args, USAGE};
use docopt::Docopt;
use ethereum_types::Address;
fn run<T: AsRef<str>>(args: &[T]) -> Args {
Docopt::new(USAGE)
.and_then(|d| d.argv(args.into_iter()).deserialize())
.unwrap()
}
#[test]
fn should_parse_all_the_options() {
let args = run(&[
"openethereum-evm",
"--json",
"--std-json",
"--std-dump-json",
"--gas",
"1",
"--gas-price",
"2",
"--from",
"0000000000000000000000000000000000000003",
"--to",
"0000000000000000000000000000000000000004",
"--code",
"05",
"--input",
"06",
"--chain",
"./testfile",
"--std-err-only",
"--std-out-only",
]);
assert_eq!(args.flag_json, true);
assert_eq!(args.flag_std_json, true);
assert_eq!(args.flag_std_dump_json, true);
assert_eq!(args.flag_std_err_only, true);
assert_eq!(args.flag_std_out_only, true);
assert_eq!(args.gas(), Ok(1.into()));
assert_eq!(args.gas_price(), Ok(2.into()));
assert_eq!(args.from(), Ok(Address::from_low_u64_be(3)));
assert_eq!(args.to(), Ok(Address::from_low_u64_be(4)));
assert_eq!(args.code(), Ok(Some(vec![05])));
assert_eq!(args.data(), Ok(Some(vec![06])));
assert_eq!(args.flag_chain, Some("./testfile".to_owned()));
}
#[test]
fn should_parse_state_test_command() {
let args = run(&[
"openethereum-evm",
"state-test",
"./file.json",
"--chain",
"homestead",
"--only=add11",
"--json",
"--std-json",
"--std-dump-json",
]);
assert_eq!(args.cmd_state_test, true);
assert!(args.arg_file.is_some());
assert_eq!(args.flag_json, true);
assert_eq!(args.flag_std_json, true);
assert_eq!(args.flag_std_dump_json, true);
assert_eq!(args.flag_chain, Some("homestead".to_owned()));
assert_eq!(args.flag_only, Some("add11".to_owned()));
}
}

141
bin/oe/account.rs Normal file
View File

@ -0,0 +1,141 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use crate::params::SpecType;
use std::num::NonZeroU32;
#[derive(Debug, PartialEq)]
pub enum AccountCmd {
New(NewAccount),
List(ListAccounts),
Import(ImportAccounts),
}
#[derive(Debug, PartialEq)]
pub struct ListAccounts {
pub path: String,
pub spec: SpecType,
}
#[derive(Debug, PartialEq)]
pub struct NewAccount {
pub iterations: NonZeroU32,
pub path: String,
pub spec: SpecType,
pub password_file: Option<String>,
}
#[derive(Debug, PartialEq)]
pub struct ImportAccounts {
pub from: Vec<String>,
pub to: String,
pub spec: SpecType,
}
#[cfg(not(feature = "accounts"))]
pub fn execute(_cmd: AccountCmd) -> Result<String, String> {
Err("Account management is deprecated. Please see #9997 for alternatives:\nhttps://github.com/openethereum/openethereum/issues/9997".into())
}
#[cfg(feature = "accounts")]
mod command {
use super::*;
use crate::{
accounts::{AccountProvider, AccountProviderSettings},
helpers::{password_from_file, password_prompt},
};
use ethstore::{accounts_dir::RootDiskDirectory, import_account, import_accounts, EthStore};
use std::path::PathBuf;
pub fn execute(cmd: AccountCmd) -> Result<String, String> {
match cmd {
AccountCmd::New(new_cmd) => new(new_cmd),
AccountCmd::List(list_cmd) => list(list_cmd),
AccountCmd::Import(import_cmd) => import(import_cmd),
}
}
fn keys_dir(path: String, spec: SpecType) -> Result<RootDiskDirectory, String> {
let spec = spec.spec(&::std::env::temp_dir())?;
let mut path = PathBuf::from(&path);
path.push(spec.data_dir);
RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e))
}
fn secret_store(
dir: Box<RootDiskDirectory>,
iterations: Option<NonZeroU32>,
) -> Result<EthStore, String> {
match iterations {
Some(i) => EthStore::open_with_iterations(dir, i),
_ => EthStore::open(dir),
}
.map_err(|e| format!("Could not open keys store: {}", e))
}
fn new(n: NewAccount) -> Result<String, String> {
let password = match n.password_file {
Some(file) => password_from_file(file)?,
None => password_prompt()?,
};
let dir = Box::new(keys_dir(n.path, n.spec)?);
let secret_store = Box::new(secret_store(dir, Some(n.iterations))?);
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
let new_account = acc_provider
.new_account(&password)
.map_err(|e| format!("Could not create new account: {}", e))?;
Ok(format!("0x{:x}", new_account))
}
fn list(list_cmd: ListAccounts) -> Result<String, String> {
let dir = Box::new(keys_dir(list_cmd.path, list_cmd.spec)?);
let secret_store = Box::new(secret_store(dir, None)?);
let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default());
let accounts = acc_provider.accounts().map_err(|e| format!("{}", e))?;
let result = accounts
.into_iter()
.map(|a| format!("0x{:x}", a))
.collect::<Vec<String>>()
.join("\n");
Ok(result)
}
fn import(i: ImportAccounts) -> Result<String, String> {
let to = keys_dir(i.to, i.spec)?;
let mut imported = 0;
for path in &i.from {
let path = PathBuf::from(path);
if path.is_dir() {
let from = RootDiskDirectory::at(&path);
imported += import_accounts(&from, &to)
.map_err(|e| format!("Importing accounts from {:?} failed: {}", path, e))?
.len();
} else if path.is_file() {
import_account(&path, &to)
.map_err(|e| format!("Importing account from {:?} failed: {}", path, e))?;
imported += 1;
}
}
Ok(format!("{} account(s) imported", imported))
}
}
#[cfg(feature = "accounts")]
pub use self::command::execute;

263
bin/oe/account_utils.rs Normal file
View File

@ -0,0 +1,263 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use crypto::publickey;
use dir::Directories;
use ethereum_types::{Address, H160};
use ethkey::Password;
use crate::params::{AccountsConfig, SpecType};
#[cfg(not(feature = "accounts"))]
mod accounts {
use super::*;
/// Dummy AccountProvider
pub struct AccountProvider;
impl ::ethcore::miner::LocalAccounts for AccountProvider {
fn is_local(&self, _address: &Address) -> bool {
false
}
}
pub fn prepare_account_provider(
_spec: &SpecType,
_dirs: &Directories,
_data_dir: &str,
_cfg: AccountsConfig,
_passwords: &[Password],
) -> Result<AccountProvider, String> {
warn!("Note: Your instance of OpenEthereum is running without account support. Some CLI options are ignored.");
Ok(AccountProvider)
}
pub fn miner_local_accounts(_: Arc<AccountProvider>) -> AccountProvider {
AccountProvider
}
pub fn miner_author(
_spec: &SpecType,
_dirs: &Directories,
_account_provider: &Arc<AccountProvider>,
_engine_signer: Address,
_passwords: &[Password],
) -> Result<Option<::ethcore::miner::Author>, String> {
Ok(None)
}
pub fn accounts_list(
_account_provider: Arc<AccountProvider>,
) -> Arc<dyn Fn() -> Vec<Address> + Send + Sync> {
Arc::new(|| vec![])
}
}
#[cfg(feature = "accounts")]
mod accounts {
use super::*;
use crate::{ethereum_types::H256, upgrade::upgrade_key_location};
use std::str::FromStr;
pub use crate::accounts::AccountProvider;
/// Pops along with error messages when a password is missing or invalid.
const VERIFY_PASSWORD_HINT: &str = "Make sure valid password is present in files passed using `--password` or in the configuration file.";
/// Initialize account provider
pub fn prepare_account_provider(
spec: &SpecType,
dirs: &Directories,
data_dir: &str,
cfg: AccountsConfig,
passwords: &[Password],
) -> Result<AccountProvider, String> {
use crate::accounts::AccountProviderSettings;
use ethstore::{accounts_dir::RootDiskDirectory, EthStore};
let path = dirs.keys_path(data_dir);
upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path);
let dir = Box::new(
RootDiskDirectory::create(&path)
.map_err(|e| format!("Could not open keys directory: {}", e))?,
);
let account_settings = AccountProviderSettings {
unlock_keep_secret: cfg.enable_fast_unlock,
blacklisted_accounts: match *spec {
SpecType::Morden
| SpecType::Ropsten
| SpecType::Kovan
| SpecType::Goerli
| SpecType::Sokol
| SpecType::Dev => vec![],
_ => vec![H160::from_str("00a329c0648769a73afac7f9381e08fb43dbea72")
.expect("the string is valid hex; qed")],
},
};
let ethstore = EthStore::open_with_iterations(dir, cfg.iterations)
.map_err(|e| format!("Could not open keys directory: {}", e))?;
if cfg.refresh_time > 0 {
ethstore.set_refresh_time(::std::time::Duration::from_secs(cfg.refresh_time));
}
let account_provider = AccountProvider::new(Box::new(ethstore), account_settings);
// Add development account if running dev chain:
if let SpecType::Dev = *spec {
insert_dev_account(&account_provider);
}
for a in cfg.unlocked_accounts {
// Check if the account exists
if !account_provider.has_account(a) {
return Err(format!(
"Account {} not found for the current chain. {}",
a,
build_create_account_hint(spec, &dirs.keys)
));
}
// Check if any passwords have been read from the password file(s)
if passwords.is_empty() {
return Err(format!(
"No password found to unlock account {}. {}",
a, VERIFY_PASSWORD_HINT
));
}
if !passwords.iter().any(|p| {
account_provider
.unlock_account_permanently(a, (*p).clone())
.is_ok()
}) {
return Err(format!(
"No valid password to unlock account {}. {}",
a, VERIFY_PASSWORD_HINT
));
}
}
Ok(account_provider)
}
pub struct LocalAccounts(Arc<AccountProvider>);
impl ::ethcore::miner::LocalAccounts for LocalAccounts {
fn is_local(&self, address: &Address) -> bool {
self.0.has_account(*address)
}
}
pub fn miner_local_accounts(account_provider: Arc<AccountProvider>) -> LocalAccounts {
LocalAccounts(account_provider)
}
pub fn miner_author(
spec: &SpecType,
dirs: &Directories,
account_provider: &Arc<AccountProvider>,
engine_signer: Address,
passwords: &[Password],
) -> Result<Option<::ethcore::miner::Author>, String> {
use ethcore::engines::EngineSigner;
// Check if engine signer exists
if !account_provider.has_account(engine_signer) {
return Err(format!(
"Consensus signer account not found for the current chain. {}",
build_create_account_hint(spec, &dirs.keys)
));
}
// Check if any passwords have been read from the password file(s)
if passwords.is_empty() {
return Err(format!(
"No password found for the consensus signer {}. {}",
engine_signer, VERIFY_PASSWORD_HINT
));
}
let mut author = None;
for password in passwords {
let signer = parity_rpc::signer::EngineSigner::new(
account_provider.clone(),
engine_signer,
password.clone(),
);
// sign dummy msg to check if password and account can be used.
if signer.sign(H256::from_low_u64_be(1)).is_ok() {
author = Some(::ethcore::miner::Author::Sealer(Box::new(signer)));
}
}
if author.is_none() {
return Err(format!(
"No valid password for the consensus signer {}. {}",
engine_signer, VERIFY_PASSWORD_HINT
));
}
Ok(author)
}
pub fn accounts_list(
account_provider: Arc<AccountProvider>,
) -> Arc<dyn Fn() -> Vec<Address> + Send + Sync> {
Arc::new(move || account_provider.accounts().unwrap_or_default())
}
fn insert_dev_account(account_provider: &AccountProvider) {
let secret = publickey::Secret::from_str(
"4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7",
)
.expect("Valid account;qed");
let dev_account = publickey::KeyPair::from_secret(secret.clone())
.expect("Valid secret produces valid key;qed");
if !account_provider.has_account(dev_account.address()) {
match account_provider.insert_account(secret, &Password::from(String::new())) {
Err(e) => warn!("Unable to add development account: {}", e),
Ok(address) => {
let _ = account_provider
.set_account_name(address.clone(), "Development Account".into());
let _ = account_provider.set_account_meta(
address,
::serde_json::to_string(
&(vec![
(
"description",
"Never use this account outside of development chain!",
),
("passwordHint", "Password is empty string"),
]
.into_iter()
.collect::<::std::collections::HashMap<_, _>>()),
)
.expect("Serialization of hashmap does not fail."),
);
}
}
}
}
// Construct an error `String` with an adaptive hint on how to create an account.
fn build_create_account_hint(spec: &SpecType, keys: &str) -> String {
format!("You can create an account via RPC, UI or `openethereum account new --chain {} --keys-path {}`.", spec, keys)
}
}
pub use self::accounts::{
accounts_list, miner_author, miner_local_accounts, prepare_account_provider, AccountProvider,
};

551
bin/oe/blockchain.rs Normal file
View File

@ -0,0 +1,551 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, io, sync::Arc, time::Instant};
use crate::{
bytes::ToPretty,
cache::CacheConfig,
db,
hash::{keccak, KECCAK_NULL_RLP},
helpers::{execute_upgrades, to_client_config},
informant::{FullNodeInformantData, Informant, MillisecondDuration},
params::{fatdb_switch_to_bool, tracing_switch_to_bool, Pruning, SpecType, Switch},
types::data_format::DataFormat,
user_defaults::UserDefaults,
};
use ansi_term::Colour;
use dir::Directories;
use ethcore::{
client::{
Balance, BlockChainClient, BlockChainReset, BlockId, DatabaseCompactionProfile,
ImportExportBlocks, Mode, Nonce, VMType,
},
miner::Miner,
verification::queue::VerifierSettings,
};
use ethcore_service::ClientService;
use ethereum_types::{Address, H256, U256};
#[derive(Debug, PartialEq)]
pub enum BlockchainCmd {
Kill(KillBlockchain),
Import(ImportBlockchain),
Export(ExportBlockchain),
ExportState(ExportState),
Reset(ResetBlockchain),
}
#[derive(Debug, PartialEq)]
pub struct ResetBlockchain {
pub dirs: Directories,
pub spec: SpecType,
pub pruning: Pruning,
pub pruning_history: u64,
pub pruning_memory: usize,
pub tracing: Switch,
pub fat_db: Switch,
pub compaction: DatabaseCompactionProfile,
pub cache_config: CacheConfig,
pub num: u32,
}
#[derive(Debug, PartialEq)]
pub struct KillBlockchain {
pub spec: SpecType,
pub dirs: Directories,
pub pruning: Pruning,
}
#[derive(Debug, PartialEq)]
pub struct ImportBlockchain {
pub spec: SpecType,
pub cache_config: CacheConfig,
pub dirs: Directories,
pub file_path: Option<String>,
pub format: Option<DataFormat>,
pub pruning: Pruning,
pub pruning_history: u64,
pub pruning_memory: usize,
pub compaction: DatabaseCompactionProfile,
pub tracing: Switch,
pub fat_db: Switch,
pub vm_type: VMType,
pub check_seal: bool,
pub with_color: bool,
pub verifier_settings: VerifierSettings,
pub max_round_blocks_to_import: usize,
}
#[derive(Debug, PartialEq)]
pub struct ExportBlockchain {
pub spec: SpecType,
pub cache_config: CacheConfig,
pub dirs: Directories,
pub file_path: Option<String>,
pub format: Option<DataFormat>,
pub pruning: Pruning,
pub pruning_history: u64,
pub pruning_memory: usize,
pub compaction: DatabaseCompactionProfile,
pub fat_db: Switch,
pub tracing: Switch,
pub from_block: BlockId,
pub to_block: BlockId,
pub check_seal: bool,
pub max_round_blocks_to_import: usize,
}
#[derive(Debug, PartialEq)]
pub struct ExportState {
pub spec: SpecType,
pub cache_config: CacheConfig,
pub dirs: Directories,
pub file_path: Option<String>,
pub format: Option<DataFormat>,
pub pruning: Pruning,
pub pruning_history: u64,
pub pruning_memory: usize,
pub compaction: DatabaseCompactionProfile,
pub fat_db: Switch,
pub tracing: Switch,
pub at: BlockId,
pub storage: bool,
pub code: bool,
pub min_balance: Option<U256>,
pub max_balance: Option<U256>,
pub max_round_blocks_to_import: usize,
}
pub fn execute(cmd: BlockchainCmd) -> Result<(), String> {
match cmd {
BlockchainCmd::Kill(kill_cmd) => kill_db(kill_cmd),
BlockchainCmd::Import(import_cmd) => execute_import(import_cmd),
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd),
BlockchainCmd::Reset(reset_cmd) => execute_reset(reset_cmd),
}
}
fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
let timer = Instant::now();
// load spec file
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
// load genesis hash
let genesis_hash = spec.genesis_header().hash();
// database paths
let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir.clone());
// user defaults path
let user_defaults_path = db_dirs.user_defaults_path();
// load user defaults
let mut user_defaults = UserDefaults::load(&user_defaults_path)?;
// select pruning algorithm
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
// check if tracing is on
let tracing = tracing_switch_to_bool(cmd.tracing, &user_defaults)?;
// check if fatdb is on
let fat_db = fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm)?;
// prepare client and snapshot paths.
let client_path = db_dirs.client_path(algorithm);
let snapshot_path = db_dirs.snapshot_path();
// execute upgrades
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
// create dirs used by parity
cmd.dirs.create_dirs(false, false)?;
// prepare client config
let mut client_config = to_client_config(
&cmd.cache_config,
spec.name.to_lowercase(),
Mode::Active,
tracing,
fat_db,
cmd.compaction,
cmd.vm_type,
"".into(),
algorithm,
cmd.pruning_history,
cmd.pruning_memory,
cmd.check_seal,
12,
);
client_config.queue.verifier_settings = cmd.verifier_settings;
let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config);
let client_db = restoration_db_handler
.open(&client_path)
.map_err(|e| format!("Failed to open database {:?}", e))?;
// build client
let service = ClientService::start(
client_config,
&spec,
client_db,
&snapshot_path,
restoration_db_handler,
&cmd.dirs.ipc_path(),
// TODO [ToDr] don't use test miner here
// (actually don't require miner at all)
Arc::new(Miner::new_for_tests(&spec, None)),
)
.map_err(|e| format!("Client service error: {:?}", e))?;
// free up the spec in memory.
drop(spec);
let client = service.client();
let instream: Box<dyn io::Read> = match cmd.file_path {
Some(f) => {
Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?)
}
None => Box::new(io::stdin()),
};
let informant = Arc::new(Informant::new(
FullNodeInformantData {
client: client.clone(),
sync: None,
net: None,
},
None,
None,
cmd.with_color,
));
service
.register_io_handler(informant)
.map_err(|_| "Unable to register informant handler".to_owned())?;
client.import_blocks(instream, cmd.format)?;
// save user defaults
user_defaults.pruning = algorithm;
user_defaults.tracing = tracing;
user_defaults.fat_db = fat_db;
user_defaults.save(&user_defaults_path)?;
let report = client.report();
let ms = timer.elapsed().as_milliseconds();
info!("Import completed in {} seconds, {} blocks, {} blk/s, {} transactions, {} tx/s, {} Mgas, {} Mgas/s",
ms / 1000,
report.blocks_imported,
(report.blocks_imported * 1000) as u64 / ms,
report.transactions_applied,
(report.transactions_applied * 1000) as u64 / ms,
report.gas_processed / 1_000_000,
(report.gas_processed / (ms * 1000)).low_u64(),
);
Ok(())
}
fn start_client(
dirs: Directories,
spec: SpecType,
pruning: Pruning,
pruning_history: u64,
pruning_memory: usize,
tracing: Switch,
fat_db: Switch,
compaction: DatabaseCompactionProfile,
cache_config: CacheConfig,
require_fat_db: bool,
max_round_blocks_to_import: usize,
) -> Result<ClientService, String> {
// load spec file
let spec = spec.spec(&dirs.cache)?;
// load genesis hash
let genesis_hash = spec.genesis_header().hash();
// database paths
let db_dirs = dirs.database(genesis_hash, None, spec.data_dir.clone());
// user defaults path
let user_defaults_path = db_dirs.user_defaults_path();
// load user defaults
let user_defaults = UserDefaults::load(&user_defaults_path)?;
// select pruning algorithm
let algorithm = pruning.to_algorithm(&user_defaults);
// check if tracing is on
let tracing = tracing_switch_to_bool(tracing, &user_defaults)?;
// check if fatdb is on
let fat_db = fatdb_switch_to_bool(fat_db, &user_defaults, algorithm)?;
if !fat_db && require_fat_db {
return Err("This command requires OpenEthereum to be synced with --fat-db on.".to_owned());
}
// prepare client and snapshot paths.
let client_path = db_dirs.client_path(algorithm);
let snapshot_path = db_dirs.snapshot_path();
// execute upgrades
execute_upgrades(&dirs.base, &db_dirs, algorithm, &compaction)?;
// create dirs used by OpenEthereum.
dirs.create_dirs(false, false)?;
// prepare client config
let client_config = to_client_config(
&cache_config,
spec.name.to_lowercase(),
Mode::Active,
tracing,
fat_db,
compaction,
VMType::default(),
"".into(),
algorithm,
pruning_history,
pruning_memory,
true,
max_round_blocks_to_import,
);
let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config);
let client_db = restoration_db_handler
.open(&client_path)
.map_err(|e| format!("Failed to open database {:?}", e))?;
let service = ClientService::start(
client_config,
&spec,
client_db,
&snapshot_path,
restoration_db_handler,
&dirs.ipc_path(),
// It's fine to use test version here,
// since we don't care about miner parameters at all
Arc::new(Miner::new_for_tests(&spec, None)),
)
.map_err(|e| format!("Client service error: {:?}", e))?;
drop(spec);
Ok(service)
}
fn execute_export(cmd: ExportBlockchain) -> Result<(), String> {
let service = start_client(
cmd.dirs,
cmd.spec,
cmd.pruning,
cmd.pruning_history,
cmd.pruning_memory,
cmd.tracing,
cmd.fat_db,
cmd.compaction,
cmd.cache_config,
false,
cmd.max_round_blocks_to_import,
)?;
let client = service.client();
let out: Box<dyn io::Write> = match cmd.file_path {
Some(f) => Box::new(
fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?,
),
None => Box::new(io::stdout()),
};
client.export_blocks(out, cmd.from_block, cmd.to_block, cmd.format)?;
info!("Export completed.");
Ok(())
}
fn execute_export_state(cmd: ExportState) -> Result<(), String> {
let service = start_client(
cmd.dirs,
cmd.spec,
cmd.pruning,
cmd.pruning_history,
cmd.pruning_memory,
cmd.tracing,
cmd.fat_db,
cmd.compaction,
cmd.cache_config,
true,
cmd.max_round_blocks_to_import,
)?;
let client = service.client();
let mut out: Box<dyn io::Write> = match cmd.file_path {
Some(f) => Box::new(
fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?,
),
None => Box::new(io::stdout()),
};
let mut last: Option<Address> = None;
let at = cmd.at;
let mut i = 0usize;
out.write_fmt(format_args!("{{ \"state\": {{",))
.expect("Couldn't write to stream.");
loop {
let accounts = client
.list_accounts(at, last.as_ref(), 1000)
.ok_or("Specified block not found")?;
if accounts.is_empty() {
break;
}
for account in accounts.into_iter() {
let balance = client
.balance(&account, at.into())
.unwrap_or_else(U256::zero);
if cmd.min_balance.map_or(false, |m| balance < m)
|| cmd.max_balance.map_or(false, |m| balance > m)
{
last = Some(account);
continue; //filtered out
}
if i != 0 {
out.write(b",").expect("Write error");
}
out.write_fmt(format_args!(
"\n\"0x{:x}\": {{\"balance\": \"{:x}\", \"nonce\": \"{:x}\"",
account,
balance,
client.nonce(&account, at).unwrap_or_else(U256::zero)
))
.expect("Write error");
let code = client
.code(&account, at.into())
.unwrap_or(None)
.unwrap_or_else(Vec::new);
if !code.is_empty() {
out.write_fmt(format_args!(", \"code_hash\": \"0x{:x}\"", keccak(&code)))
.expect("Write error");
if cmd.code {
out.write_fmt(format_args!(", \"code\": \"{}\"", code.to_hex()))
.expect("Write error");
}
}
let storage_root = client.storage_root(&account, at).unwrap_or(KECCAK_NULL_RLP);
if storage_root != KECCAK_NULL_RLP {
out.write_fmt(format_args!(", \"storage_root\": \"0x{:x}\"", storage_root))
.expect("Write error");
if cmd.storage {
out.write_fmt(format_args!(", \"storage\": {{"))
.expect("Write error");
let mut last_storage: Option<H256> = None;
loop {
let keys = client
.list_storage(at, &account, last_storage.as_ref(), 1000)
.ok_or("Specified block not found")?;
if keys.is_empty() {
break;
}
for key in keys.into_iter() {
if last_storage.is_some() {
out.write(b",").expect("Write error");
}
out.write_fmt(format_args!(
"\n\t\"0x{:x}\": \"0x{:x}\"",
key,
client
.storage_at(&account, &key, at.into())
.unwrap_or_else(Default::default)
))
.expect("Write error");
last_storage = Some(key);
}
}
out.write(b"\n}").expect("Write error");
}
}
out.write(b"}").expect("Write error");
i += 1;
if i % 10000 == 0 {
info!("Account #{}", i);
}
last = Some(account);
}
}
out.write_fmt(format_args!("\n}}}}")).expect("Write error");
info!("Export completed.");
Ok(())
}
fn execute_reset(cmd: ResetBlockchain) -> Result<(), String> {
let service = start_client(
cmd.dirs,
cmd.spec,
cmd.pruning,
cmd.pruning_history,
cmd.pruning_memory,
cmd.tracing,
cmd.fat_db,
cmd.compaction,
cmd.cache_config,
false,
0,
)?;
let client = service.client();
client.reset(cmd.num)?;
info!("{}", Colour::Green.bold().paint("Successfully reset db!"));
Ok(())
}
pub fn kill_db(cmd: KillBlockchain) -> Result<(), String> {
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
let genesis_hash = spec.genesis_header().hash();
let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir);
let user_defaults_path = db_dirs.user_defaults_path();
let mut user_defaults = UserDefaults::load(&user_defaults_path)?;
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
let dir = db_dirs.db_path(algorithm);
fs::remove_dir_all(&dir).map_err(|e| format!("Error removing database: {:?}", e))?;
user_defaults.is_first_launch = true;
user_defaults.save(&user_defaults_path)?;
info!("Database deleted.");
Ok(())
}
#[cfg(test)]
mod test {
use super::DataFormat;
#[test]
fn test_data_format_parsing() {
assert_eq!(DataFormat::Binary, "binary".parse().unwrap());
assert_eq!(DataFormat::Binary, "bin".parse().unwrap());
assert_eq!(DataFormat::Hex, "hex".parse().unwrap());
}
}

142
bin/oe/cache.rs Normal file
View File

@ -0,0 +1,142 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::cmp::max;
const MIN_BC_CACHE_MB: u32 = 4;
const MIN_DB_CACHE_MB: u32 = 8;
const MIN_BLOCK_QUEUE_SIZE_LIMIT_MB: u32 = 16;
const DEFAULT_DB_CACHE_SIZE: u32 = 128;
const DEFAULT_BC_CACHE_SIZE: u32 = 8;
const DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB: u32 = 40;
const DEFAULT_TRACE_CACHE_SIZE: u32 = 20;
const DEFAULT_STATE_CACHE_SIZE: u32 = 25;
/// Configuration for application cache sizes.
/// All values are represented in MB.
#[derive(Debug, PartialEq)]
pub struct CacheConfig {
/// Size of rocksDB cache. Almost all goes to the state column.
db: u32,
/// Size of blockchain cache.
blockchain: u32,
/// Size of transaction queue cache.
queue: u32,
/// Size of traces cache.
traces: u32,
/// Size of the state cache.
state: u32,
}
impl Default for CacheConfig {
fn default() -> Self {
CacheConfig::new(
DEFAULT_DB_CACHE_SIZE,
DEFAULT_BC_CACHE_SIZE,
DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
DEFAULT_STATE_CACHE_SIZE,
)
}
}
impl CacheConfig {
/// Creates new cache config with cumulative size equal `total`.
pub fn new_with_total_cache_size(total: u32) -> Self {
CacheConfig {
db: total * 7 / 10,
blockchain: total / 10,
queue: DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
traces: DEFAULT_TRACE_CACHE_SIZE,
state: total * 2 / 10,
}
}
/// Creates new cache config with gitven details.
pub fn new(db: u32, blockchain: u32, queue: u32, state: u32) -> Self {
CacheConfig {
db: db,
blockchain: blockchain,
queue: queue,
traces: DEFAULT_TRACE_CACHE_SIZE,
state: state,
}
}
/// Size of db cache.
pub fn db_cache_size(&self) -> u32 {
max(MIN_DB_CACHE_MB, self.db)
}
/// Size of block queue size limit
pub fn queue(&self) -> u32 {
max(self.queue, MIN_BLOCK_QUEUE_SIZE_LIMIT_MB)
}
/// Size of the blockchain cache.
pub fn blockchain(&self) -> u32 {
max(self.blockchain, MIN_BC_CACHE_MB)
}
/// Size of the traces cache.
pub fn traces(&self) -> u32 {
self.traces
}
/// Size of the state cache.
pub fn state(&self) -> u32 {
self.state * 3 / 4
}
/// Size of the jump-tables cache.
pub fn jump_tables(&self) -> u32 {
self.state / 4
}
}
#[cfg(test)]
mod tests {
use super::CacheConfig;
#[test]
fn test_cache_config_constructor() {
let config = CacheConfig::new_with_total_cache_size(200);
assert_eq!(config.db, 140);
assert_eq!(config.blockchain(), 20);
assert_eq!(config.queue(), 40);
assert_eq!(config.state(), 30);
assert_eq!(config.jump_tables(), 10);
}
#[test]
fn test_cache_config_db_cache_sizes() {
let config = CacheConfig::new_with_total_cache_size(400);
assert_eq!(config.db, 280);
assert_eq!(config.db_cache_size(), 280);
}
#[test]
fn test_cache_config_default() {
assert_eq!(
CacheConfig::default(),
CacheConfig::new(
super::DEFAULT_DB_CACHE_SIZE,
super::DEFAULT_BC_CACHE_SIZE,
super::DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB,
super::DEFAULT_STATE_CACHE_SIZE
)
);
}
}

1655
bin/oe/cli/mod.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
[parity]
no_consensus = true
chain = "dev"
[mining]
@ -10,7 +9,3 @@ min_gas_price = 0
interface = "all"
apis = ["all"]
hosts = ["all"]
[ipfs]
enable = false # this is the default
hosts = ["all"]

View File

@ -0,0 +1,4 @@
[rpc]
interface = "all"
apis = ["all"]
hosts = ["all"]

View File

@ -1,15 +1,11 @@
[network]
# Parity will try to maintain connection to at least 50 peers.
# OpenEthereum will try to maintain connection to at least 50 peers.
min_peers = 50
# Parity will maintain at most 100 peers.
# OpenEthereum will maintain at most 100 peers.
max_peers = 100
[ipc]
# You won't be able to use IPC to interact with Parity.
disable = true
[dapps]
# You won't be able to access any web Dapps.
# You won't be able to use IPC to interact with OpenEthereum.
disable = true
[mining]
@ -19,7 +15,7 @@ force_sealing = true
reseal_on_txs = "all"
# New pending block will be created only once per 4000 milliseconds.
reseal_min_period = 4000
# Parity will keep/relay at most 8192 transactions in queue.
# OpenEthereum will keep/relay at most 8192 transactions in queue.
tx_queue_size = 8192
tx_queue_per_sender = 128

View File

@ -1,5 +1,5 @@
[network]
# Parity will listen for connections on port 30305.
# OpenEthereum will listen for connections on port 30305.
port = 30305
[rpc]

View File

@ -1,18 +1,18 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// This file is part of OpenEthereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum is distributed in the hope that it will be useful,
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General 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/>.
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::io::{Error, ErrorKind};

View File

@ -2,13 +2,6 @@
mode = "last"
mode_timeout = 300
mode_alarm = 3600
auto_update = "none"
auto_update_delay = 200
auto_update_check_frequency = 50
release_track = "current"
public_node = false
no_download = false
no_consensus = false
no_persistent_txqueue = false
chain = "homestead"
@ -16,28 +9,13 @@ base_path = "$HOME/.parity"
db_path = "$HOME/.parity/chains"
keys_path = "$HOME/.parity/keys"
identity = ""
light = false
no_hardcoded_sync = false
[account]
unlock = ["0xdeadbeefcafe0000000000000000000000000000"]
password = ["~/.safe/password.file"]
keys_iterations = 10240
[private_tx]
enabled = true
signer = "0xdeadbeefcafe0000000000000000000000000000"
validators = ["0xdeadbeefcafe0000000000000000000000000000"]
passwords = "~/.safe/password.file"
account = "0xdeadbeefcafe0000000000000000000000000000"
sstore_url = "http://localhost:8082"
sstore_threshold = 0
[ui]
force = false
disable = false
port = 8180
interface = "127.0.0.1"
path = "$HOME/.parity/signer"
[network]
@ -52,7 +30,6 @@ warp = true
allow_ips = "all"
snapshot_peers = 0
max_pending_peers = 64
no_serve_light = false
reserved_only = false
reserved_peers = "./path_to_file"
@ -75,21 +52,10 @@ apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]
hosts = ["none"]
[ipc]
chmod = "660"
disable = false
path = "$HOME/.parity/jsonrpc.ipc"
apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc", "secretstore"]
[dapps]
disable = false
port = 8080
interface = "local"
hosts = ["none"]
path = "$HOME/.parity/dapps"
# authorization:
user = "test_user"
pass = "test_pass"
[secretstore]
disable = false
disable_http = false
@ -106,14 +72,6 @@ http_port = 8082
interface = "local"
port = 8083
path = "$HOME/.parity/secretstore"
cors = ["null"]
[ipfs]
enable = false
port = 5001
interface = "local"
cors = ["null"]
hosts = ["none"]
[mining]
author = "0xdeadbeefcafe0000000000000000000000000001"
@ -159,21 +117,10 @@ fat_db = "auto"
scale_verifiers = true
num_verifiers = 6
[light]
on_demand_response_time_window = 2
on_demand_request_backoff_start = 9
on_demand_request_backoff_max = 15
on_demand_request_backoff_rounds_max = 100
on_demand_request_consecutive_failures = 1
[snapshots]
disable_periodic = false
enable = false
[misc]
logging = "own_tx=trace"
log_file = "/var/log/parity.log"
log_file = "/var/log/openethereum.log"
color = true
[whisper]
enabled = false
pool_size = 20

View File

@ -8,9 +8,6 @@ chain = "./chain.json"
unlock = ["0x1", "0x2", "0x3"]
password = ["passwdfile path"]
[ui]
disable = true
[network]
warp = false
discovery = true
@ -31,24 +28,20 @@ origins = ["none"]
[rpc]
disable = true
port = 8180
server_threads = 13
[ipc]
apis = ["rpc", "eth"]
[dapps]
port = 8080
user = "username"
pass = "password"
[metrics]
enable = true
interface = "local"
port = 4000
prefix = "oe"
[secretstore]
http_port = 8082
port = 8083
[ipfs]
enable = false
port = 5001
[mining]
author = "0xdeadbeefcafe0000000000000000000000000001"
engine_signer = "0xdeadbeefcafe0000000000000000000000000001"
@ -71,23 +64,12 @@ db_compaction = "ssd"
fat_db = "off"
scale_verifiers = false
[light]
on_demand_response_time_window = 2
on_demand_request_backoff_start = 9
on_demand_request_backoff_max = 15
on_demand_request_backoff_rounds_max = 10
on_demand_request_consecutive_failures = 1
[snapshots]
disable_periodic = true
enable = false
[misc]
logging = "own_tx=trace"
log_file = "/var/log/parity.log"
log_file = "/var/log/openethereum.log"
color = true
ports_shift = 0
unsafe_expose = false
[whisper]
enabled = true
pool_size = 50

View File

@ -1,31 +1,38 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// This file is part of OpenEthereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum is distributed in the hope that it will be useful,
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General 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/>.
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
macro_rules! return_if_parse_error {
($e:expr) => (
match $e {
Err(clap_error @ ClapError { kind: ClapErrorKind::ValueValidation, .. }) => {
return Err(clap_error);
},
($e:expr) => {
match $e {
Err(
clap_error
@
ClapError {
kind: ClapErrorKind::ValueValidation,
..
},
) => {
return Err(clap_error);
}
// Otherwise, if $e is ClapErrorKind::ArgumentNotFound or Ok(),
// then convert to Option
_ => $e.ok()
}
)
// Otherwise, if $e is ClapErrorKind::ArgumentNotFound or Ok(),
// then convert to Option
_ => $e.ok(),
}
};
}
macro_rules! if_option {
@ -47,46 +54,46 @@ macro_rules! if_vec {
}
macro_rules! if_option_vec {
(Option<Vec<String>>, THEN {$then:expr} ELSE {$otherwise:expr}) => (
$then
);
(Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => (
$otherwise
);
(Option<Vec<String>>, THEN {$then:expr} ELSE {$otherwise:expr}) => {
$then
};
(Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => {
$otherwise
};
}
macro_rules! inner_option_type {
(Option<$type:ty>) => (
$type
)
(Option<$type:ty>) => {
$type
};
}
macro_rules! inner_vec_type {
(Vec<$type:ty>) => (
$type
)
(Vec<$type:ty>) => {
$type
};
}
macro_rules! inner_option_vec_type {
(Option<Vec<String>>) => (
String
)
(Option<Vec<String>>) => {
String
};
}
macro_rules! usage_with_ident {
($name:expr, $usage:expr, $help:expr) => (
if $usage.contains("<") {
format!("<{}> {} '{}'",$name, $usage, $help)
} else {
format!("[{}] {} '{}'",$name, $usage, $help)
}
);
($name:expr, $usage:expr, $help:expr) => {
if $usage.contains("<") {
format!("<{}> {} '{}'", $name, $usage, $help)
} else {
format!("[{}] {} '{}'", $name, $usage, $help)
}
};
}
macro_rules! underscore_to_hyphen {
($e:expr) => (
str::replace($e, "_", "-")
)
($e:expr) => {
str::replace($e, "_", "-")
};
}
macro_rules! usage {
@ -388,7 +395,7 @@ macro_rules! usage {
// Subcommands
let mut subcommands_wrapper = Wrapper::new(term_width).subsequent_indent(TAB);
help.push_str("parity [options]\n");
help.push_str("openethereum [options]\n");
$(
{
let mut subc_subc_exist = false;
@ -406,7 +413,7 @@ macro_rules! usage {
help.push_str(&subcommands_wrapper.fill(
format!(
"parity [options] {} {} {}\n",
"openethereum [options] {} {} {}\n",
underscore_to_hyphen!(&stringify!($subc)[4..]),
underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..]),
subc_subc_usages.join(" ")
@ -427,7 +434,7 @@ macro_rules! usage {
help.push_str(&subcommands_wrapper.fill(
format!(
"parity [options] {} {}\n",
"openethereum [options] {} {}\n",
underscore_to_hyphen!(&stringify!($subc)[4..]),
subc_usages.join(" ")
).as_ref())
@ -603,7 +610,7 @@ macro_rules! usage {
}
)*
let matches = App::new("Parity")
let matches = App::new("OpenEthereum")
.global_setting(AppSettings::VersionlessSubcommands)
.global_setting(AppSettings::DisableHelpSubcommand)
.max_term_width(MAX_TERM_WIDTH)
@ -626,7 +633,7 @@ macro_rules! usage {
.about($subc_help)
.args(&subc_usages.get(stringify!($subc)).unwrap().iter().map(|u| Arg::from_usage(u).use_delimiter(false).allow_hyphen_values(true)).collect::<Vec<Arg>>())
$(
.setting(AppSettings::SubcommandRequired) // prevent from running `parity account`
.setting(AppSettings::SubcommandRequired) // prevent from running `openethereum account`
.subcommand(
SubCommand::with_name(&underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..]))
.about($subc_subc_help)

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